@graphcommerce/next-ui 3.0.1

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 (153) hide show
  1. package/.babelrc +3 -0
  2. package/AnimatedRow/index.tsx +20 -0
  3. package/ApolloError/ApolloErrorAlert.tsx +59 -0
  4. package/ApolloError/ApolloErrorFullPage.tsx +25 -0
  5. package/AppShell/AppShellHeader/appShellHeaderContext.tsx +11 -0
  6. package/AppShell/AppShellHeader/index.tsx +433 -0
  7. package/AppShell/AppShellHeader/useAppShellHeaderContext.tsx +6 -0
  8. package/AppShell/AppShellProvider/index.tsx +18 -0
  9. package/AppShell/AppShellSticky/index.tsx +66 -0
  10. package/AppShell/AppShellTitle/index.tsx +45 -0
  11. package/AppShell/DesktopNavActions.tsx +28 -0
  12. package/AppShell/DesktopNavBar.tsx +110 -0
  13. package/AppShell/FixedFab.tsx +41 -0
  14. package/AppShell/ForwardButton.tsx +53 -0
  15. package/AppShell/FullPageShellBase.tsx +59 -0
  16. package/AppShell/Menu.tsx +7 -0
  17. package/AppShell/MenuFab.tsx +132 -0
  18. package/AppShell/MenuFabSecondaryItem.tsx +32 -0
  19. package/AppShell/MinimalPageShellBase.tsx +22 -0
  20. package/AppShell/PageShellHeader/index.tsx +14 -0
  21. package/AppShell/SheetShellBase/index.tsx +107 -0
  22. package/AppShell/SheetShellBase/useSheetStyles.ts +11 -0
  23. package/AppShell/SheetShellDragIndicator/index.tsx +48 -0
  24. package/AppShell/SheetShellHeader/index.tsx +28 -0
  25. package/AppShell/ShellBase.tsx +45 -0
  26. package/AppShell/useFabAnimation.tsx +19 -0
  27. package/AppShell/useFixedFabAnimation.tsx +18 -0
  28. package/AspectRatioContainer/index.tsx +27 -0
  29. package/Blog/BlogAuthor/index.tsx +60 -0
  30. package/Blog/BlogContent/index.tsx +24 -0
  31. package/Blog/BlogHeader/index.tsx +64 -0
  32. package/Blog/BlogList/index.tsx +27 -0
  33. package/Blog/BlogListItem/index.tsx +83 -0
  34. package/Blog/BlogTags/index.tsx +34 -0
  35. package/Blog/BlogTitle/index.tsx +29 -0
  36. package/Button/index.tsx +164 -0
  37. package/ButtonLink/index.tsx +45 -0
  38. package/CHANGELOG.md +1095 -0
  39. package/ChipMenu/index.tsx +130 -0
  40. package/ContainerWithHeader/index.tsx +49 -0
  41. package/Debug/DebugSpacer.tsx +51 -0
  42. package/FlagAvatar/index.tsx +28 -0
  43. package/Form/FormActions.tsx +15 -0
  44. package/Form/FormDivider.tsx +14 -0
  45. package/Form/FormHeader.tsx +27 -0
  46. package/Form/FormRow.tsx +14 -0
  47. package/Form/InputCheckmark.tsx +19 -0
  48. package/Form/index.tsx +62 -0
  49. package/FramerNextPagesSlider/Slide.tsx +71 -0
  50. package/FramerNextPagesSlider/Slider.tsx +39 -0
  51. package/FramerNextPagesSlider/index.ts +1 -0
  52. package/FramerNextPagesSlider/types.ts +3 -0
  53. package/FramerScroller/components/SidebarGallery.tsx +246 -0
  54. package/FramerScroller/components/SidebarSlider.tsx +103 -0
  55. package/FullPageMessage/index.tsx +68 -0
  56. package/Highlight/index.tsx +14 -0
  57. package/IconHeader/index.tsx +109 -0
  58. package/JsonLd/index.tsx +18 -0
  59. package/Page/App.tsx +15 -0
  60. package/Page/Document.tsx +23 -0
  61. package/Page/framerFeatures.ts +4 -0
  62. package/Page/types.ts +19 -0
  63. package/PageLoadIndicator/index.tsx +46 -0
  64. package/PageMeta/index.tsx +40 -0
  65. package/Pagination/index.tsx +123 -0
  66. package/RenderType/index.tsx +40 -0
  67. package/Row/ButtonLinkList/index.tsx +53 -0
  68. package/Row/ColumnOne/index.tsx +11 -0
  69. package/Row/ColumnOneBoxed/index.tsx +27 -0
  70. package/Row/ColumnOneCentered/index.tsx +22 -0
  71. package/Row/ColumnThree/index.tsx +66 -0
  72. package/Row/ColumnTwo/index.tsx +44 -0
  73. package/Row/ColumnTwoSpread/index.tsx +41 -0
  74. package/Row/ColumnTwoWithTop/index.tsx +53 -0
  75. package/Row/ContentLinks/index.tsx +48 -0
  76. package/Row/HeroBanner/index.tsx +102 -0
  77. package/Row/IconBlocks/IconBlock/index.tsx +56 -0
  78. package/Row/IconBlocks/index.tsx +57 -0
  79. package/Row/ParagraphWithSidebarSlide/index.tsx +114 -0
  80. package/Row/Quote/index.tsx +13 -0
  81. package/Row/RowImageText/index.tsx +67 -0
  82. package/Row/RowImageTextBoxed/index.tsx +75 -0
  83. package/Row/SpecialBanner/index.tsx +107 -0
  84. package/Row/index.tsx +13 -0
  85. package/SectionContainer/index.tsx +39 -0
  86. package/SectionHeader/index.tsx +69 -0
  87. package/Separator/index.tsx +33 -0
  88. package/Snackbar/ErrorSnackbar.tsx +9 -0
  89. package/Snackbar/MessageSnackbar.tsx +5 -0
  90. package/Snackbar/MessageSnackbarImpl.tsx +202 -0
  91. package/StarRatingField/index.tsx +58 -0
  92. package/Stepper/Stepper.tsx +45 -0
  93. package/StyledBadge/index.tsx +21 -0
  94. package/Styles/index.tsx +3 -0
  95. package/Styles/responsiveVal.tsx +20 -0
  96. package/SvgImage/SvgImageSimple.tsx +66 -0
  97. package/SvgImage/index.tsx +76 -0
  98. package/TextInputNumber/index.tsx +169 -0
  99. package/Theme/types.ts +63 -0
  100. package/TimeAgo/index.tsx +29 -0
  101. package/Title/index.tsx +71 -0
  102. package/ToggleButton/index.tsx +100 -0
  103. package/ToggleButtonGroup/index.tsx +112 -0
  104. package/UspList/UspListItem.tsx +46 -0
  105. package/UspList/index.tsx +32 -0
  106. package/icons/icon_addresses.svg +17 -0
  107. package/icons/icon_arrow_back.svg +1 -0
  108. package/icons/icon_arrow_forward.svg +1 -0
  109. package/icons/icon_box.svg +6 -0
  110. package/icons/icon_chat.svg +1 -0
  111. package/icons/icon_checkmark.svg +1 -0
  112. package/icons/icon_checkmark_green.svg +1 -0
  113. package/icons/icon_chevron_back.svg +8 -0
  114. package/icons/icon_chevron_down.svg +8 -0
  115. package/icons/icon_chevron_left.svg +8 -0
  116. package/icons/icon_chevron_right.svg +8 -0
  117. package/icons/icon_chevron_up.svg +8 -0
  118. package/icons/icon_close.svg +6 -0
  119. package/icons/icon_close_circle.svg +1 -0
  120. package/icons/icon_collapse_vertical.svg +11 -0
  121. package/icons/icon_customer_service.svg +6 -0
  122. package/icons/icon_email.svg +1 -0
  123. package/icons/icon_email_outline.svg +6 -0
  124. package/icons/icon_expand_vertical.svg +11 -0
  125. package/icons/icon_heart.svg +15 -0
  126. package/icons/icon_home.svg +6 -0
  127. package/icons/icon_id.svg +6 -0
  128. package/icons/icon_invoice_red.svg +7 -0
  129. package/icons/icon_location_red.svg +7 -0
  130. package/icons/icon_lock.svg +6 -0
  131. package/icons/icon_menu.svg +1 -0
  132. package/icons/icon_min.svg +1 -0
  133. package/icons/icon_newspaper.svg +6 -0
  134. package/icons/icon_party.svg +7 -0
  135. package/icons/icon_person.svg +6 -0
  136. package/icons/icon_person_alt.svg +6 -0
  137. package/icons/icon_person_alt_big.svg +15 -0
  138. package/icons/icon_phone.svg +1 -0
  139. package/icons/icon_plus.svg +1 -0
  140. package/icons/icon_sad_face.svg +11 -0
  141. package/icons/icon_sad_shoppingbag.svg +16 -0
  142. package/icons/icon_search.svg +1 -0
  143. package/icons/icon_shopping_bag.svg +7 -0
  144. package/icons/icon_shutdown.svg +6 -0
  145. package/icons/icon_star.svg +6 -0
  146. package/icons/icon_star_filled_muted.svg +6 -0
  147. package/icons/icon_star_yellow.svg +6 -0
  148. package/icons/index.tsx +42 -0
  149. package/index.ts +163 -0
  150. package/package.json +61 -0
  151. package/tsconfig.json +5 -0
  152. package/types.d.ts +13 -0
  153. package/useIntersectionObserver/index.tsx +31 -0
@@ -0,0 +1,130 @@
1
+ import { Chip, ChipProps, makeStyles, Menu, Theme } from '@material-ui/core'
2
+ import clsx from 'clsx'
3
+ import React, { PropsWithChildren, useState } from 'react'
4
+ import { iconChevronDown, iconChevronUp, iconCloseCircle } from '../icons'
5
+ import SectionHeader from '../SectionHeader'
6
+ import responsiveVal from '../Styles/responsiveVal'
7
+ import SvgImage from '../SvgImage'
8
+
9
+ export const useChipMenuStyles = makeStyles(
10
+ (theme: Theme) => ({
11
+ /*
12
+ !importants: ensure background #xxxxxx on hover, focus etc regardless given props to the component
13
+ otherwise you'll get: ".MuiChip-deletable.MuiChip-outlined:hover" which is prone to changes and thereby a fragile selector
14
+ */
15
+ chip: {
16
+ background: theme.palette.background.default,
17
+ '& .MuiChip-label': {
18
+ wordWrap: 'break-word',
19
+ },
20
+ '&:hover': {
21
+ background: `${theme.palette.background.highlight} !important`,
22
+ },
23
+ '&:focus': {
24
+ background: `${theme.palette.background.default} !important`,
25
+ },
26
+ },
27
+ chipSelected: {
28
+ border: `1px solid ${theme.palette.text.primary}`,
29
+ background: theme.palette.grey['100'],
30
+ color: theme.palette.text.primary,
31
+ '&:hover': {
32
+ background: `${theme.palette.background.default} !important`,
33
+ borderColor: theme.palette.grey['600'],
34
+ },
35
+ '&:focus': {
36
+ background: `${theme.palette.grey['100']} !important`,
37
+ },
38
+ },
39
+ menuPaper: {
40
+ minWidth: responsiveVal(200, 560),
41
+ maxWidth: 560,
42
+ marginTop: theme.spacings.xxs,
43
+ padding: `${theme.spacings.xs} ${theme.spacings.xs}`,
44
+ [theme.breakpoints.down('xs')]: {
45
+ minWidth: 0,
46
+ width: '100%',
47
+ maxWidth: `calc(100% - (${theme.page.horizontal} * 2))`,
48
+ margin: '0 auto',
49
+ marginTop: '8px',
50
+ },
51
+ },
52
+ menuList: {
53
+ padding: 0,
54
+ '&:focus': {
55
+ outline: 'none',
56
+ },
57
+ },
58
+ }),
59
+ { name: 'ChipMenu' },
60
+ )
61
+
62
+ export type ChipMenuProps = PropsWithChildren<Omit<ChipProps, 'children'>> & {
63
+ selectedLabel?: React.ReactNode
64
+ selected: boolean
65
+ onClose?: () => void
66
+ labelRight?: React.ReactNode
67
+ }
68
+
69
+ export default function ChipMenu(props: ChipMenuProps) {
70
+ const { children, selected, onDelete, label, labelRight, onClose, selectedLabel, ...chipProps } =
71
+ props
72
+ const [openEl, setOpenEl] = useState<null | HTMLElement>(null)
73
+ const classes = useChipMenuStyles(props)
74
+
75
+ let deleteIcon = selected ? (
76
+ <SvgImage size='medium' src={iconCloseCircle} alt='close' shade='default' />
77
+ ) : (
78
+ <SvgImage
79
+ size='medium'
80
+ src={iconChevronDown}
81
+ alt='chevron down'
82
+ loading='eager'
83
+ shade='muted'
84
+ />
85
+ )
86
+ if (openEl)
87
+ deleteIcon = (
88
+ <SvgImage size='medium' src={iconChevronUp} alt='chevron up' loading='eager' shade='muted' />
89
+ )
90
+
91
+ const selectedAndMenuHidden = selected && !openEl && selectedLabel
92
+
93
+ return (
94
+ <>
95
+ <Chip
96
+ variant='default'
97
+ color={selected || openEl ? 'primary' : 'default'}
98
+ clickable
99
+ onDelete={onDelete || ((event) => setOpenEl(event.currentTarget.parentElement))}
100
+ onClick={(event) => {
101
+ setOpenEl(event.currentTarget)
102
+ }}
103
+ deleteIcon={deleteIcon}
104
+ {...chipProps}
105
+ label={selectedLabel ?? label}
106
+ className={clsx(
107
+ classes.chip,
108
+ chipProps.className,
109
+ selectedAndMenuHidden && classes.chipSelected,
110
+ )}
111
+ />
112
+
113
+ <Menu
114
+ anchorEl={openEl}
115
+ open={!!openEl}
116
+ onClose={() => {
117
+ if (onClose) onClose()
118
+ setOpenEl(null)
119
+ }}
120
+ getContentAnchorEl={null} // https://github.com/mui-org/material-ui/issues/7961#issuecomment-326116559
121
+ anchorPosition={{ top: 6, left: 0 }}
122
+ anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
123
+ classes={{ paper: classes.menuPaper, list: classes.menuList }}
124
+ >
125
+ <SectionHeader labelLeft={label ?? ''} labelRight={labelRight ?? ''} usePadding />
126
+ {children}
127
+ </Menu>
128
+ </>
129
+ )
130
+ }
@@ -0,0 +1,49 @@
1
+ import { makeStyles, Theme, Typography } from '@material-ui/core'
2
+ import React from 'react'
3
+ import Row from '../Row'
4
+ import { UseStyles } from '../Styles'
5
+
6
+ const useStyles = makeStyles(
7
+ (theme: Theme) => ({
8
+ head: {
9
+ display: 'grid',
10
+ justifyContent: 'space-between',
11
+ gridTemplateColumns: 'auto auto',
12
+ alignItems: 'end',
13
+ marginBottom: theme.spacings.md,
14
+ },
15
+ title: {
16
+ lineHeight: 1,
17
+ textTransform: 'uppercase',
18
+ },
19
+ right: {
20
+ lineHeight: 1,
21
+ },
22
+ }),
23
+ { name: 'ContainerWithHeader' },
24
+ )
25
+
26
+ export type ContainerWithHeaderProps = {
27
+ title: string
28
+ rightArea: React.ReactNode
29
+ children: React.ReactNode
30
+ } & UseStyles<typeof useStyles>
31
+
32
+ export default function ContainerWithHeader(props: ContainerWithHeaderProps) {
33
+ const { title, rightArea, children } = props
34
+ const classes = useStyles(props)
35
+
36
+ return (
37
+ <Row>
38
+ <div className={classes.head}>
39
+ <Typography variant='h3' component='h2' className={classes.title}>
40
+ {title}
41
+ </Typography>
42
+ <Typography component='div' variant='subtitle1' className={classes.right}>
43
+ {rightArea}
44
+ </Typography>
45
+ </div>
46
+ {children}
47
+ </Row>
48
+ )
49
+ }
@@ -0,0 +1,51 @@
1
+ import { makeStyles } from '@material-ui/core'
2
+ import { CSSProperties } from '@material-ui/styles'
3
+ import { AnimatePresence, HTMLMotionProps, m } from 'framer-motion'
4
+
5
+ export type DebugSpacerProps = {
6
+ height?: number
7
+ color?: CSSProperties['color']
8
+ } & HTMLMotionProps<'div'>
9
+
10
+ const useStyles = makeStyles(() => ({
11
+ root: {
12
+ background: `repeating-linear-gradient(-45deg, transparent, transparent 20px, #f6f6f6 20px, #f6f6f6 40px) top left`,
13
+ boxShadow: `#eee 0px 0px 0px 3px inset`,
14
+ overflow: 'hidden',
15
+ },
16
+ div: {
17
+ color: `rgba(0,0,0,0.1)`,
18
+ overflow: 'hidden',
19
+ width: '100%',
20
+ textAlign: 'center',
21
+ fontSize: 20,
22
+ display: 'grid',
23
+ alignItems: 'center',
24
+ },
25
+ }))
26
+ export default function DebugSpacer({ height = 250, ...divProps }: DebugSpacerProps) {
27
+ const classes = useStyles()
28
+ let increment = 0
29
+ // eslint-disable-next-line no-return-assign
30
+ const rows = new Array(Math.ceil(height / 100)).fill(0).map(() => {
31
+ increment += 100
32
+ return Math.min(increment, height)
33
+ })
34
+
35
+ return (
36
+ <m.div className={classes.root} {...divProps} animate={{ height }}>
37
+ <AnimatePresence>
38
+ {rows.map((nr) => (
39
+ <div
40
+ data-key={`debug-row-${nr}`}
41
+ className={classes.div}
42
+ style={{ height: nr % 100 || 100, borderTop: `3px solid #efefef` }}
43
+ key={`debug-row-${nr}`}
44
+ >
45
+ {nr}
46
+ </div>
47
+ ))}
48
+ </AnimatePresence>
49
+ </m.div>
50
+ )
51
+ }
@@ -0,0 +1,28 @@
1
+ import { makeStyles, Theme, Avatar, AvatarProps } from '@material-ui/core'
2
+ import React from 'react'
3
+
4
+ const useStyles = makeStyles(
5
+ (theme: Theme) => ({
6
+ root: {},
7
+ }),
8
+ { name: 'FlagAvatar' },
9
+ )
10
+
11
+ export type FlagAvatarProps = { country: string } & Omit<AvatarProps, 'src'>
12
+
13
+ export default function FlagAvatar(props: FlagAvatarProps) {
14
+ const { country, ...avatarProps } = props
15
+ const classes = useStyles(props)
16
+
17
+ return (
18
+ <Avatar
19
+ {...avatarProps}
20
+ classes={classes}
21
+ imgProps={{ loading: 'lazy' }}
22
+ alt=''
23
+ src={`https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.3/flags/4x3/${country}.svg`}
24
+ >
25
+ {country.toLocaleUpperCase()}
26
+ </Avatar>
27
+ )
28
+ }
@@ -0,0 +1,15 @@
1
+ import { styled } from '@material-ui/core'
2
+
3
+ const FormActions = styled('div')(
4
+ ({ theme }) => ({
5
+ paddingTop: theme.spacings.xxs,
6
+ paddingBottom: theme.spacings.md,
7
+ justifyContent: 'center',
8
+ display: 'grid',
9
+ gridAutoFlow: 'column',
10
+ gap: `calc(${theme.spacings.xxs} * 2)`,
11
+ }),
12
+ { name: 'FormActions' },
13
+ )
14
+
15
+ export default FormActions
@@ -0,0 +1,14 @@
1
+ import { styled } from '@material-ui/core'
2
+
3
+ const FormDivider = styled('div')(
4
+ ({ theme }) => ({
5
+ background: theme.palette.divider,
6
+ height: 1,
7
+ width: '100%',
8
+ marginTop: theme.spacings.xxs,
9
+ marginBottom: theme.spacings.xxs,
10
+ }),
11
+ { name: 'FormDivider' },
12
+ )
13
+
14
+ export default FormDivider
@@ -0,0 +1,27 @@
1
+ import { makeStyles, Theme, Typography, TypographyProps } from '@material-ui/core'
2
+ import React from 'react'
3
+ import { UseStyles } from '../Styles'
4
+
5
+ const useStyles = makeStyles(
6
+ (theme: Theme) => ({
7
+ heading: {
8
+ marginBottom: `calc(${theme.spacings.xxs} * -1)`,
9
+ marginTop: theme.spacings.xxs,
10
+ },
11
+ }),
12
+ { name: 'FormHeader' },
13
+ )
14
+
15
+ export type FormHeaderProps = TypographyProps &
16
+ UseStyles<typeof useStyles> & { children: React.ReactNode }
17
+
18
+ export default function FormHeader(props: FormHeaderProps) {
19
+ const { children, ...typographyProps } = props
20
+ const classes = useStyles(props)
21
+
22
+ return (
23
+ <Typography {...typographyProps} className={classes.heading}>
24
+ {children}
25
+ </Typography>
26
+ )
27
+ }
@@ -0,0 +1,14 @@
1
+ import { styled } from '@material-ui/core'
2
+
3
+ const FormRow = styled('div')(
4
+ ({ theme }) => ({
5
+ paddingTop: theme.spacings.xxs,
6
+ paddingBottom: theme.spacings.xxs,
7
+ display: 'grid',
8
+ gridTemplateColumns: `repeat(auto-fit, minmax(200px, 1fr))`,
9
+ gap: `calc(${theme.spacings.xxs} * 2)`,
10
+ }),
11
+ { name: 'FormRow' },
12
+ )
13
+
14
+ export default FormRow
@@ -0,0 +1,19 @@
1
+ import React, { PropsWithChildren } from 'react'
2
+ import SvgImage from '../SvgImage'
3
+ import { iconCheckmarkGreen } from '../icons'
4
+
5
+ export type InputCheckmarkProps = PropsWithChildren<{ show?: boolean }>
6
+
7
+ /**
8
+ * When the `valid` prop is passed it will render a CheckIcon, else it will render children.
9
+ *
10
+ * ```typescript
11
+ * ;<InputCheckmark valid>Fallback things</InputCheckmark>
12
+ * ```
13
+ */
14
+ export default function InputCheckmark(props: InputCheckmarkProps) {
15
+ const { show: valid, children } = props
16
+
17
+ if (!valid) return <>{children}</>
18
+ return <SvgImage src={iconCheckmarkGreen} alt='checkmark' />
19
+ }
package/Form/index.tsx ADDED
@@ -0,0 +1,62 @@
1
+ import { makeStyles, Theme } from '@material-ui/core'
2
+ import clsx from 'clsx'
3
+ import React from 'react'
4
+ import { UseStyles } from '../Styles'
5
+
6
+ const useStyles = makeStyles(
7
+ (theme: Theme) => ({
8
+ root: {
9
+ display: 'grid',
10
+ alignItems: 'center',
11
+ padding: `${theme.spacings.xxs} 0`,
12
+ },
13
+ secondary: {
14
+ background: theme.palette.secondary.light,
15
+ },
16
+ default: {
17
+ background: theme.palette.background.highlight,
18
+ },
19
+ contained: {
20
+ padding: theme.spacings.sm,
21
+ overflow: 'hidden',
22
+ borderRadius: 6,
23
+ },
24
+ }),
25
+ { name: 'Form' },
26
+ )
27
+
28
+ export type BaseFormProps = {
29
+ contained?: boolean
30
+ background?: 'secondary' | 'default'
31
+ children: React.ReactNode
32
+ } & UseStyles<typeof useStyles>
33
+
34
+ export type FormFormProps = BaseFormProps & JSX.IntrinsicElements['form']
35
+
36
+ export const Form = React.forwardRef<HTMLFormElement, FormFormProps>((props, ref) => {
37
+ const classes = useStyles(props)
38
+ const { contained, background, ...formProps } = props
39
+
40
+ return (
41
+ <form ref={ref} {...formProps} className={clsx(classes.root, contained && classes.contained)} />
42
+ )
43
+ })
44
+
45
+ export type DivFormProps = BaseFormProps & JSX.IntrinsicElements['div']
46
+
47
+ export const FormDiv = React.forwardRef<HTMLDivElement, DivFormProps>((props, ref) => {
48
+ const classes = useStyles(props)
49
+ const { contained, background = 'default', ...formProps } = props
50
+
51
+ return (
52
+ <div
53
+ ref={ref}
54
+ {...formProps}
55
+ className={clsx(
56
+ classes.root,
57
+ contained && classes.contained,
58
+ contained && classes[background],
59
+ )}
60
+ />
61
+ )
62
+ })
@@ -0,0 +1,71 @@
1
+ import { makeStyles } from '@material-ui/core'
2
+ import { usePageContext, usePageRouter } from '@graphcommerce/framer-next-pages'
3
+ import { SPRING_ANIM } from '@graphcommerce/framer-sheet'
4
+ import { m, motionValue, useElementScroll } from 'framer-motion'
5
+ import React, { useEffect, useRef } from 'react'
6
+ import { ScrollPositions } from './types'
7
+
8
+ const useStyles = makeStyles(
9
+ () => ({
10
+ slide: {
11
+ position: 'absolute',
12
+ left: 0,
13
+ top: 0,
14
+ right: 0,
15
+ height: '100%',
16
+ overflowY: 'auto',
17
+ },
18
+ }),
19
+ { name: 'FramerNextPagesSlider' },
20
+ )
21
+
22
+ type Custom = {
23
+ depth: number
24
+ direction: 1 | -1
25
+ exiting: boolean
26
+ }
27
+
28
+ export type RouterSlideProps = {
29
+ children?: React.ReactNode
30
+ scrollPositions: React.MutableRefObject<ScrollPositions>
31
+ }
32
+
33
+ export default function RouterSlide({ children, scrollPositions }: RouterSlideProps) {
34
+ const classes = useStyles()
35
+ const ref = useRef<HTMLDivElement>(null)
36
+ const { scrollY } = useElementScroll(ref)
37
+ const { direction, depth } = usePageContext()
38
+ const router = usePageRouter()
39
+
40
+ useEffect(() => {
41
+ scrollPositions.current[router.asPath] ??= motionValue(0)
42
+ scrollY.onChange((v) => scrollPositions.current[router.asPath].set(v))
43
+ }, [router.asPath, scrollPositions, scrollY])
44
+
45
+ useEffect(() => {
46
+ const scroll = scrollPositions.current[router.asPath].get()
47
+ if (ref.current && scroll) ref.current.scrollTop = scroll
48
+ }, [router.asPath, scrollPositions])
49
+
50
+ return (
51
+ <m.div
52
+ className={classes.slide}
53
+ ref={ref}
54
+ custom={{ depth, direction, exiting: false }}
55
+ initial='initial'
56
+ animate={{ x: 0 }}
57
+ exit='exit'
58
+ variants={{
59
+ initial: (c: Custom) => (c.direction === 1 ? { x: '100%' } : { x: '-100%' }),
60
+ exit: (c: Custom) => {
61
+ if (!c.depth && c.exiting && c.direction === 1) return { x: '-100%' }
62
+ if (!c.depth && c.exiting && c.direction === -1) return { x: '100%' }
63
+ return { x: 0 }
64
+ },
65
+ }}
66
+ transition={{ ...SPRING_ANIM, restDelta: 0.01, damping: 20 }}
67
+ >
68
+ {children}
69
+ </m.div>
70
+ )
71
+ }
@@ -0,0 +1,39 @@
1
+ import { makeStyles } from '@material-ui/styles'
2
+ import { usePageContext, usePageRouter } from '@graphcommerce/framer-next-pages'
3
+ import { AnimatePresence } from 'framer-motion'
4
+ import React, { useRef } from 'react'
5
+ import RouterSlide from './Slide'
6
+ import { ScrollPositions } from './types'
7
+
8
+ const useStyles = makeStyles(
9
+ {
10
+ slider: {
11
+ position: 'relative',
12
+ width: '100%',
13
+ height: '100%',
14
+ },
15
+ },
16
+ { name: 'FramerNextPagesSlider' },
17
+ )
18
+
19
+ export type SliderRouteProps = { children?: React.ReactNode }
20
+
21
+ /** RouteSlider provides an iOS settings style navigation */
22
+ export default function Slider(props: SliderRouteProps) {
23
+ const classes = useStyles()
24
+ const { children } = props
25
+ const { depth, direction } = usePageContext()
26
+ const router = usePageRouter()
27
+
28
+ const scrollPositions = useRef<ScrollPositions>({})
29
+
30
+ return (
31
+ <div className={classes.slider}>
32
+ <AnimatePresence initial={false} custom={{ depth, direction, exiting: true }}>
33
+ <RouterSlide key={router.asPath} scrollPositions={scrollPositions}>
34
+ {children}
35
+ </RouterSlide>
36
+ </AnimatePresence>
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1 @@
1
+ export { default } from './Slider'
@@ -0,0 +1,3 @@
1
+ import { MotionValue } from 'framer-motion'
2
+
3
+ export type ScrollPositions = { [key: string]: MotionValue<number> }