@graphcommerce/magento-compare 6.2.0-canary.9

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/Config.graphqls +17 -0
  3. package/components/CompareFab.tsx +100 -0
  4. package/components/CompareListAttributes.tsx +45 -0
  5. package/components/CompareListForm.tsx +83 -0
  6. package/components/CompareListIntroText.tsx +21 -0
  7. package/components/CompareListItems.tsx +28 -0
  8. package/components/CompareListRow.tsx +62 -0
  9. package/components/CompareListRowMoreInformation.tsx +58 -0
  10. package/components/CompareListSelect.tsx +77 -0
  11. package/components/CompareMessageSnackbar.tsx +59 -0
  12. package/components/CompareProductButton.tsx +59 -0
  13. package/components/CompareProductToggle.tsx +155 -0
  14. package/components/EmptyCompareList.tsx +19 -0
  15. package/components/EmptyCompareListButton.tsx +58 -0
  16. package/components/index.ts +13 -0
  17. package/graphql/AddProductsToCompareList.graphql +5 -0
  18. package/graphql/AssignCustomerToCompareList.graphql +5 -0
  19. package/graphql/ComparableItem.graphql +11 -0
  20. package/graphql/CompareList.graphql +5 -0
  21. package/graphql/CompareListFragment.graphql +12 -0
  22. package/graphql/CompareProductIdInternal.graphql +6 -0
  23. package/graphql/CompareSummary.graphql +5 -0
  24. package/graphql/CompareSummaryFragment.graphql +7 -0
  25. package/graphql/CreateCompareList.graphql +5 -0
  26. package/graphql/CurrentCompareUid.graphql +6 -0
  27. package/graphql/CurrentCompareUid.graphqls +7 -0
  28. package/graphql/CustomerCompareList.graphql +7 -0
  29. package/graphql/DeleteCompareList.graphql +5 -0
  30. package/graphql/RemoveProductsFromCompareList.graphql +5 -0
  31. package/graphql/index.ts +12 -0
  32. package/hooks/index.ts +6 -0
  33. package/hooks/useAssignCurrentCompareListUid.ts +16 -0
  34. package/hooks/useClearCurrentCompareListUid.ts +15 -0
  35. package/hooks/useCompareList.ts +10 -0
  36. package/hooks/useCompareListStyles.ts +21 -0
  37. package/hooks/useCompareListUidCreate.ts +27 -0
  38. package/hooks/useCompareSummary.ts +13 -0
  39. package/index.ts +3 -0
  40. package/next-env.d.ts +4 -0
  41. package/package.json +38 -0
  42. package/plugins/AddCompareFabNextToCart.tsx +19 -0
  43. package/plugins/AddCompareToProductPage.tsx +36 -0
  44. package/plugins/AddCompareTypePolicies.tsx +25 -0
  45. package/plugins/CompareAbleProductListItem.tsx +41 -0
  46. package/tsconfig.json +5 -0
  47. package/typePolicies.ts +12 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # @graphcommerce/magento-compare
2
+
3
+ ## 6.2.0-canary.9
@@ -0,0 +1,17 @@
1
+ enum CompareVariant {
2
+ ICON
3
+ CHECKBOX
4
+ }
5
+
6
+ extend input GraphCommerceConfig {
7
+ """
8
+ Use compare functionality
9
+ """
10
+ compare: Boolean
11
+
12
+ """
13
+ By default the compare feature is denoted with a 'compare ICON' (2 arrows facing one another).
14
+ This may be fine for experienced users, but for more clarity it's also possible to present the compare feature as a CHECKBOX accompanied by the 'Compare' label
15
+ """
16
+ compareVariant: CompareVariant
17
+ }
@@ -0,0 +1,100 @@
1
+ import {
2
+ extendableComponent,
3
+ IconSvg,
4
+ useFabSize,
5
+ iconCompare,
6
+ useScrollY,
7
+ } from '@graphcommerce/next-ui'
8
+ import { i18n } from '@lingui/core'
9
+ import { Trans } from '@lingui/react'
10
+ import { styled, Box, SxProps, Theme, NoSsr, Badge, Button, ButtonProps } from '@mui/material'
11
+ import { m, useTransform } from 'framer-motion'
12
+ import React from 'react'
13
+ import { useCompareSummary } from '../hooks'
14
+
15
+ export type CompareFabProps = {
16
+ icon?: React.ReactNode
17
+ sx?: SxProps<Theme>
18
+ } & Pick<ButtonProps, 'color' | 'size' | 'variant'>
19
+
20
+ type CompareFabContentProps = CompareFabProps & { total_quantity: number }
21
+
22
+ const MotionDiv = styled(m.div)({})
23
+
24
+ const MotionFab = m(
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ React.forwardRef<any, Omit<ButtonProps, 'style' | 'onDrag'>>((props, ref) => (
27
+ <Button variant='pill' {...props} ref={ref} />
28
+ )),
29
+ )
30
+
31
+ const { classes } = extendableComponent('CompareFab', ['root', 'compare', 'shadow'] as const)
32
+
33
+ function CompareFabContent(props: CompareFabContentProps) {
34
+ const { total_quantity, icon, sx = [], ...rest } = props
35
+ const scrollY = useScrollY()
36
+ const opacity = useTransform(scrollY, [50, 60], [0, 1])
37
+
38
+ const compareIcon = icon ?? <IconSvg src={iconCompare} size='large' />
39
+ const fabIconSize = useFabSize('responsive')
40
+
41
+ if (total_quantity === 0) return null
42
+
43
+ return (
44
+ <Box
45
+ className={classes.root}
46
+ sx={[{ position: 'relative', height: fabIconSize }, ...(Array.isArray(sx) ? sx : [sx])]}
47
+ >
48
+ <Badge
49
+ color='primary'
50
+ variant='standard'
51
+ overlap='circular'
52
+ badgeContent={total_quantity}
53
+ sx={{ height: '100%', '& .MuiBadge-badge': { zIndex: 2000 } }}
54
+ >
55
+ <MotionFab
56
+ href='/compare'
57
+ className={classes.compare}
58
+ aria-label={i18n._(/* i18n */ 'Compare')}
59
+ color='inherit'
60
+ sx={(theme) => ({
61
+ width: 'unset',
62
+ backgroundColor: `${theme.palette.background.paper} !important`,
63
+ [theme.breakpoints.down('md')]: {},
64
+ })}
65
+ {...rest}
66
+ >
67
+ {compareIcon} <Trans id='Compare' />
68
+ </MotionFab>
69
+ </Badge>
70
+
71
+ <MotionDiv
72
+ className={classes.shadow}
73
+ sx={(theme) => ({
74
+ pointerEvents: 'none',
75
+ borderRadius: '99em',
76
+ position: 'absolute',
77
+ height: '100%',
78
+ width: '100%',
79
+ boxShadow: 5,
80
+ top: 0,
81
+ [theme.breakpoints.down('md')]: {
82
+ opacity: '1 !important',
83
+ },
84
+ })}
85
+ style={{ opacity }}
86
+ />
87
+ </Box>
88
+ )
89
+ }
90
+
91
+ export function CompareFab(props: CompareFabProps) {
92
+ const compareList = useCompareSummary()
93
+ const totalQuantity = compareList.data?.compareList?.item_count ?? 0
94
+
95
+ return (
96
+ <NoSsr fallback={<CompareFabContent total_quantity={0} {...props} />}>
97
+ {totalQuantity > 0 && <CompareFabContent total_quantity={totalQuantity} {...props} />}
98
+ </NoSsr>
99
+ )
100
+ }
@@ -0,0 +1,45 @@
1
+ import { filterNonNullableKeys } from '@graphcommerce/next-ui'
2
+ import { Box, SxProps, Theme } from '@mui/material'
3
+ import { useCompareList } from '../hooks/useCompareList'
4
+ import { useCompareVisibleItems } from './CompareListForm'
5
+ import { CompareListRow } from './CompareListRow'
6
+ import { CompareListRowMoreInformation } from './CompareListRowMoreInformation'
7
+
8
+ export type CompareListAttributesProps = {
9
+ sx?: SxProps<Theme>
10
+ }
11
+
12
+ export function CompareListAttributes(props: CompareListAttributesProps) {
13
+ const { sx } = props
14
+ const compareList = useCompareList()
15
+ const compareListAttributes = filterNonNullableKeys(compareList.data?.compareList?.attributes)
16
+ const items = useCompareVisibleItems()
17
+
18
+ return (
19
+ <Box
20
+ sx={[
21
+ (theme) => ({
22
+ bgcolor: theme.palette.background.default,
23
+ '& :first-of-type > div': {
24
+ mt: 0,
25
+ },
26
+ mx: `calc(${theme.page.horizontal} * -1)`,
27
+ py: theme.spacings.md,
28
+ px: theme.page.horizontal,
29
+ [theme.breakpoints.up('lg')]: {
30
+ borderRadius: theme.shape.borderRadius * 1.5,
31
+ mx: `calc(${theme.spacings.lg} * -1)`,
32
+ p: theme.spacings.lg,
33
+ },
34
+ }),
35
+ ...(Array.isArray(sx) ? sx : [sx]),
36
+ ]}
37
+ >
38
+ {compareListAttributes.map((attribute) => (
39
+ <CompareListRow compareAbleItems={items} attribute={attribute} key={attribute?.code} />
40
+ ))}
41
+
42
+ <CompareListRowMoreInformation compareAbleItems={items} />
43
+ </Box>
44
+ )
45
+ }
@@ -0,0 +1,83 @@
1
+ import { useForm, useFormPersist, UseFormReturn, useWatch } from '@graphcommerce/ecommerce-ui'
2
+ import { filterNonNullableKeys, nonNullable } from '@graphcommerce/next-ui'
3
+ import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react'
4
+ import { useCompareList } from '../hooks'
5
+
6
+ type CompareListFormProps = { children?: React.ReactNode }
7
+
8
+ type FormFields = { selected: number[] }
9
+
10
+ type CompareFormContextType = Omit<UseFormReturn<FormFields>, 'formState' | 'watch'> & {
11
+ selectedPrevious: React.MutableRefObject<number[]>
12
+ }
13
+
14
+ const CompareFormContext = createContext<CompareFormContextType | undefined>(undefined)
15
+
16
+ export function CompareListForm(props: CompareListFormProps) {
17
+ const { children } = props
18
+
19
+ const compareList = useCompareList()
20
+ const compareListData = compareList.data
21
+ const compareListCount = compareListData?.compareList?.item_count ?? 0
22
+ const gridColumns = compareListCount <= 3 ? compareListCount : 3
23
+
24
+ const form = useForm<FormFields>({
25
+ defaultValues: { selected: [...Array(gridColumns).keys()] },
26
+ })
27
+
28
+ useFormPersist({ form, name: 'CompareList', storage: 'localStorage' })
29
+ const selectedState = form.watch('selected')
30
+ const selectedPrevious = useRef<number[]>(selectedState)
31
+ const compareAbleItems = compareListData?.compareList?.items
32
+
33
+ useEffect(() => {
34
+ if (compareAbleItems?.length) {
35
+ selectedPrevious.current = selectedState
36
+
37
+ /*
38
+ * It's possible that the user has 5 items in his comparelist, so [0,1,2,3,4] are all selectable indexes
39
+ * If the user has a selected state of indexes [0,3,4] but then removes the 4th item, the currentCompareProducts[4] would be undefined and the UI would be corrupt
40
+ * So we need to get the first index that isnt already in the selectedState array (as we cant have duplicates)
41
+ */
42
+ selectedState.forEach((selectedIndex, index) => {
43
+ if (selectedIndex >= compareAbleItems.length) {
44
+ const allIndexes = [...Array(compareAbleItems.length).keys()]
45
+ const allowedIndexes = allIndexes.filter((el) => !selectedState.includes(el))
46
+ form.setValue(`selected.${index}`, allowedIndexes[0])
47
+ }
48
+ })
49
+
50
+ // if there are less items in the compare list than in our selectedState
51
+ if (compareListCount < selectedState.length) {
52
+ form.setValue(`selected`, [...Array(compareListCount).keys()])
53
+ }
54
+
55
+ // if there are less items in our selectedState than we have columns
56
+ if (selectedState.length < gridColumns) {
57
+ form.setValue(`selected`, [...Array(gridColumns).keys()])
58
+ }
59
+ }
60
+ }, [compareAbleItems?.length, compareListCount, form, gridColumns, selectedState])
61
+
62
+ const value = useMemo(
63
+ () => ({ ...form, selectedPrevious } satisfies CompareFormContextType),
64
+ [form],
65
+ )
66
+ return <CompareFormContext.Provider value={value}>{children}</CompareFormContext.Provider>
67
+ }
68
+
69
+ export function useCompareForm() {
70
+ const context = useContext(CompareFormContext)
71
+ if (!context) throw Error('useCompareForm must be used inside a CompareForm')
72
+ return context
73
+ }
74
+
75
+ export function useCompareVisibleItems() {
76
+ const { control } = useCompareForm()
77
+ const selected = useWatch<FormFields>({ control, name: 'selected' })
78
+
79
+ const selectedState = Array.isArray(selected) ? selected : [selected]
80
+
81
+ const compareList = filterNonNullableKeys(useCompareList().data?.compareList?.items)
82
+ return selectedState.map((i) => compareList[i]).filter(nonNullable)
83
+ }
@@ -0,0 +1,21 @@
1
+ import { Box, Container } from '@mui/material'
2
+
3
+ type CompareListIntroTextProps = {
4
+ children: React.ReactNode
5
+ }
6
+
7
+ export function CompareListIntroText({ children }: CompareListIntroTextProps) {
8
+ return (
9
+ <Box
10
+ sx={(theme) => ({
11
+ position: 'relative',
12
+ textAlign: 'center',
13
+ paddingBottom: 2,
14
+ background: theme.palette.background.paper,
15
+ zIndex: 11,
16
+ })}
17
+ >
18
+ <Container>{children}</Container>
19
+ </Box>
20
+ )
21
+ }
@@ -0,0 +1,28 @@
1
+ import {
2
+ AddProductsToCartForm,
3
+ ProductItemsGridProps,
4
+ ProductListItemsBase,
5
+ } from '@graphcommerce/magento-product'
6
+ import { useCompareListStyles } from '../hooks/useCompareListStyles'
7
+ import { useCompareVisibleItems } from './CompareListForm'
8
+
9
+ export type CompareListItemsProps = Pick<ProductItemsGridProps, 'renderers' | 'sx'>
10
+
11
+ export function CompareListItems(props: CompareListItemsProps) {
12
+ const { renderers, sx } = props
13
+
14
+ const compareListStyles = useCompareListStyles()
15
+
16
+ const items = useCompareVisibleItems().map((i) => i.product)
17
+
18
+ return (
19
+ <AddProductsToCartForm>
20
+ <ProductListItemsBase
21
+ title='Compare items'
22
+ items={items}
23
+ renderers={renderers}
24
+ sx={[compareListStyles, ...(Array.isArray(sx) ? sx : [sx])]}
25
+ />
26
+ </AddProductsToCartForm>
27
+ )
28
+ }
@@ -0,0 +1,62 @@
1
+ import { SectionContainer } from '@graphcommerce/next-ui'
2
+ import { Box } from '@mui/material'
3
+ import { ComparableItemFragment } from '../graphql'
4
+ import { useCompareListStyles } from '../hooks/useCompareListStyles'
5
+
6
+ export type CompareRowProps = {
7
+ compareAbleItems: ComparableItemFragment[]
8
+ attribute: {
9
+ code: string
10
+ label: string
11
+ } | null
12
+ }
13
+
14
+ export function CompareListRow(props: CompareRowProps) {
15
+ const { attribute, compareAbleItems } = props
16
+ const columnCount = compareAbleItems.length <= 3 ? compareAbleItems.length : 3
17
+
18
+ const compareListStyles = useCompareListStyles()
19
+
20
+ return (
21
+ <Box>
22
+ <SectionContainer
23
+ labelLeft={attribute?.label}
24
+ sx={(theme) => ({
25
+ '& .SectionHeader-root': {
26
+ justifyContent: 'center',
27
+ borderBottom: 'none',
28
+ pb: 0,
29
+ '& > .MuiTypography-root': {
30
+ pb: theme.spacings.xxs,
31
+ borderBottom: `1px solid ${theme.palette.divider}`,
32
+ width: `calc(calc(calc(100% / 3) * ${columnCount}) + ${
33
+ columnCount > 1 ? theme.spacings.md : '0px'
34
+ })`,
35
+ [theme.breakpoints.down('md')]: {
36
+ width: '100%',
37
+ },
38
+ },
39
+ },
40
+ })}
41
+ >
42
+ <Box sx={[compareListStyles, (theme) => ({ mb: theme.spacings.lg })]}>
43
+ {compareAbleItems?.map((item, idx) => (
44
+ <Box
45
+ // eslint-disable-next-line react/no-array-index-key
46
+ key={idx}
47
+ sx={{
48
+ '& > p:first-of-type': { marginTop: 0 },
49
+ '& > p:last-of-type': { marginBottom: 0 },
50
+ }}
51
+ dangerouslySetInnerHTML={{
52
+ __html:
53
+ item?.attributes.find((itemAttribute) => itemAttribute?.code === attribute?.code)
54
+ ?.value ?? '',
55
+ }}
56
+ />
57
+ ))}
58
+ </Box>
59
+ </SectionContainer>
60
+ </Box>
61
+ )
62
+ }
@@ -0,0 +1,58 @@
1
+ import { productLink } from '@graphcommerce/magento-product'
2
+ import { Button, iconChevronRight, IconSvg, SectionContainer } from '@graphcommerce/next-ui'
3
+ import { Trans } from '@lingui/react'
4
+ import { Box } from '@mui/material'
5
+ import { useCompareListStyles } from '../hooks/useCompareListStyles'
6
+ import { CompareRowProps } from './CompareListRow'
7
+
8
+ export type CompareListRowMoreInformationProps = Pick<CompareRowProps, 'compareAbleItems'>
9
+
10
+ export function CompareListRowMoreInformation(props: CompareListRowMoreInformationProps) {
11
+ const { compareAbleItems } = props
12
+ const columnCount = compareAbleItems.length <= 3 ? compareAbleItems.length : 3
13
+
14
+ const compareListStyles = useCompareListStyles()
15
+
16
+ return (
17
+ <Box>
18
+ <SectionContainer
19
+ labelLeft={<Trans id='More information' />}
20
+ sx={(theme) => ({
21
+ '& .SectionHeader-root': {
22
+ justifyContent: 'center',
23
+ borderBottom: 'none',
24
+ pb: 0,
25
+ '& > .MuiTypography-root': {
26
+ pb: theme.spacings.xxs,
27
+ borderBottom: `1px solid ${theme.palette.divider}`,
28
+ width: `calc(calc(calc(100% / 3) * ${columnCount}) + ${
29
+ columnCount > 1 ? theme.spacings.md : '0px'
30
+ })`,
31
+ [theme.breakpoints.down('md')]: {
32
+ width: '100%',
33
+ },
34
+ },
35
+ },
36
+ })}
37
+ >
38
+ <Box sx={compareListStyles}>
39
+ {compareAbleItems?.map((item) => {
40
+ if (!item?.product) return null
41
+ return (
42
+ <Box key={item.uid}>
43
+ <Button
44
+ variant='inline'
45
+ href={productLink(item?.product)}
46
+ endIcon={<IconSvg key='icon' src={iconChevronRight} size='inherit' />}
47
+ sx={{ justifyContent: 'flex-start' }}
48
+ >
49
+ <Trans id='View Product' />
50
+ </Button>
51
+ </Box>
52
+ )
53
+ })}
54
+ </Box>
55
+ </SectionContainer>
56
+ </Box>
57
+ )
58
+ }
@@ -0,0 +1,77 @@
1
+ import { SelectElement } from '@graphcommerce/ecommerce-ui'
2
+ import { useMotionValueValue } from '@graphcommerce/framer-utils'
3
+ import { useScrollY } from '@graphcommerce/next-ui'
4
+ import { Box, Container, FormControl, SxProps, Theme, useTheme } from '@mui/material'
5
+ import { useCompareList, useCompareListStyles } from '../hooks'
6
+ import { useCompareForm } from './CompareListForm'
7
+
8
+ export type CompareListSelectProps = {
9
+ bgColor?: 'default' | 'paper'
10
+ sx?: SxProps<Theme>
11
+ }
12
+
13
+ export function CompareListSelect(props: CompareListSelectProps) {
14
+ const { bgColor = 'paper', sx = [] } = props
15
+
16
+ const compareList = useCompareList().data?.compareList
17
+ const compareListCount = compareList?.item_count ?? 0
18
+ const gridColumns = compareListCount <= 3 ? compareListCount : 3
19
+ const compareAbleItems = compareList?.items ?? []
20
+
21
+ const { control, selectedPrevious, setValue } = useCompareForm()
22
+
23
+ const compareListStyles = useCompareListStyles()
24
+ const theme2 = useTheme()
25
+ const scrollY = useScrollY()
26
+ const scrolled = useMotionValueValue(scrollY, (y) => ({
27
+ xs:
28
+ y >
29
+ parseInt(theme2.appShell.headerHeightSm, 10) -
30
+ parseInt(theme2.appShell.headerHeightSm, 10) * 0.5,
31
+ md: y > parseInt(theme2.appShell.headerHeightSm, 10),
32
+ }))
33
+
34
+ return (
35
+ <Box
36
+ sx={[
37
+ (theme) => ({
38
+ pt: 1,
39
+ pb: theme.spacings.xxs,
40
+ background: theme.palette.background[bgColor],
41
+ position: 'sticky',
42
+ zIndex: 10,
43
+ top: {
44
+ xs: `calc(${theme.appShell.headerHeightSm} / 2)`,
45
+ lg: theme.page.vertical,
46
+ },
47
+ boxShadow: {
48
+ xs: scrolled.xs ? theme.shadows[1] : 'none',
49
+ md: scrolled.md ? theme.shadows[1] : 'none',
50
+ },
51
+ }),
52
+ ...(Array.isArray(sx) ? sx : [sx]),
53
+ ]}
54
+ >
55
+ <Container sx={{ ...compareListStyles }}>
56
+ {[...Array(gridColumns).keys()].map((compareSelectIndex) => (
57
+ <FormControl key={compareSelectIndex}>
58
+ <SelectElement
59
+ control={control}
60
+ name={`selected.${compareSelectIndex}`}
61
+ options={compareAbleItems.map((i, id) => ({
62
+ id,
63
+ label: i?.product?.name ?? '',
64
+ }))}
65
+ size='small'
66
+ onChange={(to) => {
67
+ const from = selectedPrevious.current?.[compareSelectIndex]
68
+ const found = selectedPrevious.current?.indexOf(Number(to))
69
+ if (found > -1) setValue(`selected.${found}`, from)
70
+ }}
71
+ />
72
+ </FormControl>
73
+ ))}
74
+ </Container>
75
+ </Box>
76
+ )
77
+ }
@@ -0,0 +1,59 @@
1
+ import { Button, iconChevronRight, IconSvg, MessageSnackbar } from '@graphcommerce/next-ui'
2
+ import { Trans } from '@lingui/react'
3
+ import { SetStateAction } from 'react'
4
+
5
+ type CompareMessageSnackbarProps = {
6
+ count: number | undefined
7
+ name: string | null | undefined
8
+ displayMessageBar: boolean
9
+ setDisplayMessageBar: (value: SetStateAction<boolean>) => void
10
+ }
11
+
12
+ export function CompareMessageSnackbar(props: CompareMessageSnackbarProps) {
13
+ const { count, name, displayMessageBar, setDisplayMessageBar } = props
14
+
15
+ return (
16
+ <MessageSnackbar
17
+ open={displayMessageBar}
18
+ onMouseDown={(e) => {
19
+ e.stopPropagation()
20
+ }}
21
+ onClick={(e) => {
22
+ e.stopPropagation()
23
+ }}
24
+ onClose={() => {
25
+ setDisplayMessageBar(false)
26
+ }}
27
+ variant='pill'
28
+ action={
29
+ count && count > 1 ? (
30
+ <Button
31
+ href='/compare'
32
+ id='view-wishlist-button'
33
+ size='medium'
34
+ variant='pill'
35
+ color='secondary'
36
+ fullWidth
37
+ endIcon={<IconSvg src={iconChevronRight} />}
38
+ >
39
+ <Trans id='View comparison' />
40
+ </Button>
41
+ ) : null
42
+ }
43
+ >
44
+ <>
45
+ <Trans
46
+ id='<0>{name}</0> has been added to your comparison!'
47
+ components={{ 0: <strong /> }}
48
+ values={{ name }}
49
+ />
50
+ {count === 1 ? (
51
+ <>
52
+ {' '}
53
+ <Trans id='Add another product to start comparing.' />
54
+ </>
55
+ ) : null}
56
+ </>
57
+ </MessageSnackbar>
58
+ )
59
+ }
@@ -0,0 +1,59 @@
1
+ import { useMutation } from '@graphcommerce/graphql'
2
+ import { Trans } from '@lingui/react'
3
+ import { Badge, Box, Button, Checkbox, SxProps, Theme } from '@mui/material'
4
+ import { useState } from 'react'
5
+ import {
6
+ AddProductsToCompareListDocument,
7
+ CompareProductIdInternalFragment,
8
+ RemoveProductsFromCompareListDocument,
9
+ } from '../graphql'
10
+ import { useCompareSummary } from '../hooks'
11
+ import { useCompareListUidCreate } from '../hooks/useCompareListUidCreate'
12
+ import { CompareMessageSnackbar } from './CompareMessageSnackbar'
13
+
14
+ type CompareProductButtonProps = CompareProductIdInternalFragment & { sx?: SxProps<Theme> }
15
+
16
+ export function CompareProductButton(props: CompareProductButtonProps) {
17
+ const { compare_product_id, name, sx } = props
18
+ const idString = String(compare_product_id)
19
+ const create = useCompareListUidCreate()
20
+ const compareList = useCompareSummary()
21
+ const inCompareList =
22
+ compareList.data?.compareList?.items?.some((i) => i?.uid === idString) ?? false
23
+ const [add] = useMutation(AddProductsToCompareListDocument)
24
+ const [remove] = useMutation(RemoveProductsFromCompareListDocument)
25
+ const [displayMessageBar, setDisplayMessageBar] = useState(false)
26
+
27
+ const handleClick: React.MouseEventHandler<HTMLButtonElement> = async (e) => {
28
+ if (inCompareList) {
29
+ await remove({ variables: { products: [idString], uid: await create() } })
30
+ } else {
31
+ await add({ variables: { products: [idString], uid: await create() } })
32
+ setDisplayMessageBar(true)
33
+ }
34
+ }
35
+
36
+ return (
37
+ <Box sx={[...(Array.isArray(sx) ? sx : [sx])]}>
38
+ <Badge badgeContent={compareList.data?.compareList?.item_count} color='primary'>
39
+ <Button
40
+ variant='contained'
41
+ onClick={handleClick}
42
+ onMouseDown={(e) => e.stopPropagation()}
43
+ startIcon={<Checkbox checked={inCompareList} />}
44
+ >
45
+ {inCompareList ? <Trans id='In comparelist' /> : <Trans id='Compare' />}
46
+ </Button>
47
+ </Badge>
48
+
49
+ {displayMessageBar && (
50
+ <CompareMessageSnackbar
51
+ displayMessageBar={displayMessageBar}
52
+ setDisplayMessageBar={setDisplayMessageBar}
53
+ count={compareList.data?.compareList?.item_count}
54
+ name={name}
55
+ />
56
+ )}
57
+ </Box>
58
+ )
59
+ }