@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,246 @@
1
+ import { Fab, makeStyles, Theme, useTheme } from '@material-ui/core'
2
+ import {
3
+ ScrollerButton,
4
+ ScrollerDots,
5
+ ScrollerProvider,
6
+ Scroller,
7
+ MotionImageAspectProps,
8
+ CenterSlide,
9
+ MotionImageAspect,
10
+ } from '@graphcommerce/framer-scroller'
11
+ import { clientSize, useMotionValueValue } from '@graphcommerce/framer-utils'
12
+ import clsx from 'clsx'
13
+ import { m } from 'framer-motion'
14
+ import React, { useState } from 'react'
15
+ import { UseStyles } from '../../Styles'
16
+ import responsiveVal from '../../Styles/responsiveVal'
17
+ import SvgImage from '../../SvgImage'
18
+ import SvgImageSimple from '../../SvgImage/SvgImageSimple'
19
+ import {
20
+ iconChevronLeft,
21
+ iconChevronRight,
22
+ iconCollapseVertical,
23
+ iconExpandVertical,
24
+ } from '../../icons'
25
+
26
+ type StyleProps = {
27
+ aspectRatio: [number, number]
28
+ clientHeight: number
29
+ }
30
+
31
+ const useStyles = makeStyles(
32
+ (theme: Theme) => ({
33
+ root: {
34
+ display: 'grid',
35
+ [theme.breakpoints.up('md')]: {
36
+ gridTemplateColumns: '1fr auto',
37
+ },
38
+ background: theme.palette.background.highlight,
39
+ paddingRight: `calc((100% - ${theme.breakpoints.values.lg}px) / 2)`,
40
+ marginBottom: theme.spacings.lg,
41
+ },
42
+ rootZoomed: {
43
+ position: 'relative',
44
+ zIndex: theme.zIndex.modal,
45
+ marginTop: 0,
46
+ [theme.breakpoints.up('md')]: {
47
+ marginTop: `calc(${theme.page.headerInnerHeight.md} * -1 - ${theme.spacings.sm} * 2)`,
48
+ },
49
+ paddingRight: 0,
50
+ },
51
+ scrollerContainer: ({ aspectRatio: [width, height] }: StyleProps) => {
52
+ const headerHeight = `${theme.page.headerInnerHeight.sm} - ${theme.spacings.sm} * 2`
53
+ const galleryMargin = theme.spacings.lg
54
+ const extraSpacing = theme.spacings.md
55
+
56
+ const maxHeight = `calc(100vh - ${headerHeight} - ${galleryMargin} - ${extraSpacing})`
57
+ const ratio = `calc(${height} / ${width} * 100%)`
58
+ return {
59
+ height: 0, // https://stackoverflow.com/questions/44770074/css-grid-row-height-safari-bug
60
+ position: 'relative',
61
+ minHeight: '100%',
62
+ paddingTop: `min(${ratio}, ${maxHeight})`,
63
+ borderRadius: 2,
64
+ [theme.breakpoints.down('sm')]: {
65
+ width: '100vw',
66
+ },
67
+ }
68
+ },
69
+ scrollerContainerZoomed: ({ clientHeight }: StyleProps) => ({
70
+ paddingTop: clientHeight,
71
+ }),
72
+ scroller: {
73
+ position: 'absolute',
74
+ top: 0,
75
+ width: '100%',
76
+ height: '100%',
77
+ display: `grid`,
78
+ gridAutoFlow: `column`,
79
+ gridTemplateColumns: `repeat(100, 100%)`,
80
+ gridTemplateRows: `100%`,
81
+ },
82
+
83
+ sidebarWrapper: {
84
+ boxSizing: 'content-box',
85
+ display: 'grid',
86
+ justifyItems: 'start',
87
+ alignContent: 'center',
88
+ position: 'relative',
89
+ [theme.breakpoints.up('md')]: {
90
+ width: `calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
91
+ theme.page.horizontal
92
+ } * 2)`,
93
+ },
94
+ },
95
+ sidebarWrapperZoomed: {
96
+ [theme.breakpoints.up('md')]: {
97
+ marginLeft: `calc((${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
98
+ theme.page.horizontal
99
+ } * 2) * -1)`,
100
+ left: `calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
101
+ theme.page.horizontal
102
+ } * 2)`,
103
+ },
104
+ },
105
+ sidebar: {
106
+ boxSizing: 'border-box',
107
+ width: '100%',
108
+ padding: `${theme.spacings.md} ${theme.page.horizontal}`,
109
+ [theme.breakpoints.up('md')]: {
110
+ paddingLeft: theme.spacings.lg,
111
+ },
112
+ },
113
+ bottomCenter: {
114
+ display: 'grid',
115
+ gridAutoFlow: 'column',
116
+ gap: theme.spacings.xxs,
117
+ position: 'absolute',
118
+ bottom: theme.spacings.xxs,
119
+ justifyContent: 'center',
120
+ width: '100%',
121
+ pointerEvents: 'none',
122
+ '& > *': {
123
+ pointerEvents: 'all',
124
+ },
125
+ },
126
+ sliderButtons: {
127
+ [theme.breakpoints.down('sm')]: {
128
+ display: 'none',
129
+ },
130
+ },
131
+ toggleIcon: {
132
+ boxShadow: theme.shadows[2],
133
+ },
134
+ topRight: {
135
+ display: 'grid',
136
+ gridAutoFlow: 'column',
137
+ top: theme.spacings.sm,
138
+ gap: theme.spacings.xxs,
139
+ position: 'absolute',
140
+ right: theme.spacings.sm,
141
+ },
142
+ centerLeft: {
143
+ display: 'grid',
144
+ gridAutoFlow: 'row',
145
+ gap: theme.spacings.xxs,
146
+ position: 'absolute',
147
+ left: theme.spacings.sm,
148
+ top: `calc(50% - 28px)`,
149
+ },
150
+ centerRight: {
151
+ display: 'grid',
152
+ gap: theme.spacings.xxs,
153
+ position: 'absolute',
154
+ right: theme.spacings.sm,
155
+ top: `calc(50% - 28px)`,
156
+ },
157
+ }),
158
+ { name: 'SidebarGallery' },
159
+ )
160
+
161
+ export type SidebarGalleryProps = {
162
+ sidebar: React.ReactNode
163
+ images: MotionImageAspectProps[]
164
+ aspectRatio?: [number, number]
165
+ } & UseStyles<typeof useStyles>
166
+
167
+ export default function SidebarGallery(props: SidebarGalleryProps) {
168
+ const { sidebar, images, aspectRatio = [1, 1] } = props
169
+ const [zoomed, setZoomed] = useState(false)
170
+
171
+ const clientHeight = useMotionValueValue(clientSize.y, (y) => y)
172
+ const classes = useStyles({ clientHeight, aspectRatio })
173
+
174
+ const toggle = () => {
175
+ setZoomed(!zoomed)
176
+ if (!zoomed) {
177
+ document.body.style.overflow = 'hidden'
178
+ window.scrollTo({ top: 0, behavior: 'smooth' })
179
+ }
180
+ }
181
+
182
+ const clsxZoom = (key: string) => clsx(classes?.[key], zoomed && classes?.[`${key}Zoomed`])
183
+
184
+ const theme = useTheme()
185
+
186
+ return (
187
+ <ScrollerProvider scrollSnapAlign='center'>
188
+ <m.div layout className={clsxZoom('root')}>
189
+ <m.div
190
+ layout
191
+ className={clsxZoom('scrollerContainer')}
192
+ onLayoutAnimationComplete={() => {
193
+ if (!zoomed) document.body.style.overflow = ''
194
+ }}
195
+ >
196
+ <Scroller className={clsxZoom('scroller')} hideScrollbar>
197
+ {images.map((image, idx) => (
198
+ <CenterSlide key={typeof image.src === 'string' ? image.src : idx}>
199
+ <MotionImageAspect
200
+ layout
201
+ src={image.src}
202
+ width={image.width}
203
+ height={image.height}
204
+ loading={idx === 0 ? 'eager' : 'lazy'}
205
+ sizes={{
206
+ 0: '100vw',
207
+ [theme.breakpoints.values.md]: zoomed ? '100vw' : '60vw',
208
+ }}
209
+ dontReportWronglySizedImages
210
+ />
211
+ </CenterSlide>
212
+ ))}
213
+ </Scroller>
214
+ <m.div layout className={classes.topRight}>
215
+ <Fab color='inherit' size='small' className={classes.toggleIcon} onClick={toggle}>
216
+ {!zoomed ? (
217
+ <SvgImage src={iconExpandVertical} alt='Zoom in' loading='eager' />
218
+ ) : (
219
+ <SvgImage src={iconCollapseVertical} alt='Zoom out' loading='eager' />
220
+ )}
221
+ </Fab>
222
+ </m.div>
223
+ <div className={classes.centerLeft}>
224
+ <ScrollerButton layout direction='left' size='small' className={classes.sliderButtons}>
225
+ <SvgImageSimple src={iconChevronLeft} />
226
+ </ScrollerButton>
227
+ </div>
228
+ <div className={classes.centerRight}>
229
+ <ScrollerButton layout direction='right' size='small' className={classes.sliderButtons}>
230
+ <SvgImageSimple src={iconChevronRight} />
231
+ </ScrollerButton>
232
+ </div>
233
+
234
+ <div className={classes.bottomCenter}>
235
+ <ScrollerDots layout />
236
+ </div>
237
+ </m.div>
238
+ <div className={clsxZoom('sidebarWrapper')}>
239
+ <m.div layout className={clsxZoom('sidebar')}>
240
+ {sidebar}
241
+ </m.div>
242
+ </div>
243
+ </m.div>
244
+ </ScrollerProvider>
245
+ )
246
+ }
@@ -0,0 +1,103 @@
1
+ import { relative } from 'path/posix'
2
+ import { Theme } from '@material-ui/core'
3
+ import { makeStyles } from '@material-ui/styles'
4
+ import {
5
+ Scroller,
6
+ ScrollerButton,
7
+ ScrollerPageCounter,
8
+ ScrollerProvider,
9
+ } from '@graphcommerce/framer-scroller'
10
+ import React, { ReactNode } from 'react'
11
+ import { SvgImageSimple, iconChevronLeft, iconChevronRight } from '../..'
12
+ import { UseStyles } from '../../Styles'
13
+ import responsiveVal from '../../Styles/responsiveVal'
14
+
15
+ const useStyles = makeStyles(
16
+ (theme: Theme) => ({
17
+ root: {
18
+ display: 'grid',
19
+ gridTemplateColumns: 'minmax(150px, 25%) 1fr',
20
+ maxWidth: '100%',
21
+ marginBottom: `${theme.spacings.xl}`,
22
+ },
23
+ sidebar: {
24
+ display: 'grid',
25
+ alignContent: 'space-between',
26
+ padding: `0 ${theme.spacings.lg} 0 ${theme.page.horizontal}`,
27
+ },
28
+ scrollerContainer: {
29
+ position: 'relative',
30
+ minWidth: 1,
31
+ },
32
+ scroller: {
33
+ width: '100%',
34
+ display: 'grid',
35
+ gridAutoFlow: 'column',
36
+ gridColumnGap: theme.spacings.md,
37
+ gridRowGap: theme.spacings.lg,
38
+ alignContent: 'space-around',
39
+ paddingRight: theme.page.horizontal,
40
+
41
+ '& > *': {
42
+ minWidth: responsiveVal(200, 400),
43
+ },
44
+ },
45
+ sliderButtons: {
46
+ [theme.breakpoints.down('sm')]: {
47
+ display: 'none',
48
+ },
49
+ },
50
+ centerLeft: {
51
+ display: 'grid',
52
+ gridAutoFlow: 'row',
53
+ gap: theme.spacings.xxs,
54
+ position: 'absolute',
55
+ left: theme.spacings.sm,
56
+ top: `calc(50% - 28px)`,
57
+ },
58
+ centerRight: {
59
+ display: 'grid',
60
+ gap: theme.spacings.xxs,
61
+ position: 'absolute',
62
+ right: theme.spacings.sm,
63
+ top: `calc(50% - 28px)`,
64
+ },
65
+ }),
66
+ { name: 'SidebarSlider' },
67
+ )
68
+
69
+ export type SidebarSliderProps = { children: ReactNode; sidebar: ReactNode } & UseStyles<
70
+ typeof useStyles
71
+ >
72
+
73
+ export default function SidebarSlider(props: SidebarSliderProps) {
74
+ const { children, sidebar } = props
75
+ const classes = useStyles(props)
76
+
77
+ return (
78
+ <ScrollerProvider scrollSnapAlign='start'>
79
+ <div className={classes.root}>
80
+ <div className={classes.sidebar}>
81
+ <div>{sidebar}</div>
82
+ <ScrollerPageCounter />
83
+ </div>
84
+
85
+ <div className={classes.scrollerContainer}>
86
+ <Scroller className={classes.scroller} hideScrollbar>
87
+ {children}
88
+ </Scroller>
89
+ <div className={classes.centerLeft}>
90
+ <ScrollerButton layout direction='left' className={classes.sliderButtons}>
91
+ <SvgImageSimple src={iconChevronLeft} />
92
+ </ScrollerButton>
93
+ </div>
94
+ <div className={classes.centerRight}>
95
+ <ScrollerButton layout direction='right' className={classes.sliderButtons}>
96
+ <SvgImageSimple src={iconChevronRight} />
97
+ </ScrollerButton>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </ScrollerProvider>
102
+ )
103
+ }
@@ -0,0 +1,68 @@
1
+ import { Container, Theme, Typography } from '@material-ui/core'
2
+ import { makeStyles } from '@material-ui/styles'
3
+ import React from 'react'
4
+ import PageMeta from '../PageMeta'
5
+ import responsiveVal from '../Styles/responsiveVal'
6
+
7
+ const useStyles = makeStyles(
8
+ (theme: Theme) => ({
9
+ root: {
10
+ marginTop: responsiveVal(50, 250),
11
+ alignItems: 'center',
12
+ },
13
+ subject: {
14
+ textAlign: 'center',
15
+ margin: `${theme.spacings.sm} 0`,
16
+ },
17
+ description: {
18
+ marginTop: 8,
19
+ },
20
+ innerContainer: {
21
+ display: 'grid',
22
+ alignItems: 'center',
23
+ justifyItems: 'center',
24
+ },
25
+ button: {
26
+ marginTop: 6,
27
+ },
28
+ }),
29
+ {
30
+ name: 'FullPageMessage',
31
+ },
32
+ )
33
+
34
+ export type FullPageMessageProps = {
35
+ icon: React.ReactNode
36
+ title: React.ReactNode
37
+ description?: React.ReactNode
38
+ button?: React.ReactNode
39
+ altButton?: React.ReactNode
40
+ }
41
+
42
+ export default function FullPageMessage(props: FullPageMessageProps) {
43
+ const { icon, title, description, button, altButton } = props
44
+ const classes = useStyles()
45
+
46
+ return (
47
+ <div className={classes.root}>
48
+ <Container maxWidth='md' className={classes.innerContainer}>
49
+ <PageMeta title='Account' metaDescription='Account Dashboard' metaRobots={['noindex']} />
50
+ {icon}
51
+
52
+ <div className={classes.subject}>
53
+ <Typography component='h2' variant='h4'>
54
+ {title}
55
+ </Typography>
56
+ {description && (
57
+ <Typography component='p' variant='body1' className={classes.description}>
58
+ {description}
59
+ </Typography>
60
+ )}
61
+ </div>
62
+
63
+ <div>{button}</div>
64
+ <div className={classes.button}>{altButton}</div>
65
+ </Container>
66
+ </div>
67
+ )
68
+ }
@@ -0,0 +1,14 @@
1
+ export default function Highlight(props: { text: string; highlight: string }) {
2
+ const { text, highlight } = props
3
+ const start = text.toLocaleLowerCase().indexOf(highlight.toLocaleLowerCase())
4
+
5
+ if (start < 0) return <>{text}</>
6
+
7
+ return (
8
+ <>
9
+ {text.slice(0, start)}
10
+ <strong>{text.slice(start, highlight.length + start)}</strong>
11
+ {text.slice(start + highlight.length)}
12
+ </>
13
+ )
14
+ }
@@ -0,0 +1,109 @@
1
+ import { makeStyles, Theme, Typography } from '@material-ui/core'
2
+ import clsx from 'clsx'
3
+ import React from 'react'
4
+ import responsiveVal from '../Styles/responsiveVal'
5
+ import SvgImage, { SvgImageProps } from '../SvgImage'
6
+
7
+ // TODO: remove all occurrences. deprecated component
8
+
9
+ const useStyles = makeStyles(
10
+ (theme: Theme) => ({
11
+ container: {
12
+ textAlign: 'center',
13
+ fontSize: responsiveVal(16, 24),
14
+ },
15
+ innerContainer: {
16
+ display: 'flex',
17
+ alignItems: 'center',
18
+ justifyContent: 'center',
19
+ gap: 4,
20
+ },
21
+ breakColumnsDesktop: {
22
+ [theme.breakpoints.up('md')]: {
23
+ display: 'unset',
24
+ },
25
+ },
26
+ margin: {
27
+ marginTop: theme.spacings.sm,
28
+ marginBottom: theme.spacings.sm,
29
+ },
30
+ ellipsis: {
31
+ whiteSpace: 'nowrap',
32
+ overflow: 'hidden',
33
+ textOverflow: 'ellipsis',
34
+ },
35
+ mediumFontWeight: {
36
+ fontWeight: 700,
37
+ },
38
+ }),
39
+ { name: 'IconHeader' },
40
+ )
41
+
42
+ export type IconHeaderSize = 'small' | 'medium' | 'large'
43
+
44
+ type IconHeaderProps = {
45
+ title: string
46
+ size?: IconHeaderSize
47
+ iconSize?: number
48
+ iconSizeMobile?: number
49
+ noMargin?: boolean
50
+ stayInline?: boolean
51
+ ellipsis?: boolean
52
+ } & Pick<SvgImageProps, 'src' | 'alt'>
53
+
54
+ type IconHeaderHeadings = 'h2' | 'h4' | 'h5'
55
+
56
+ export default function IconHeader(props: IconHeaderProps) {
57
+ const {
58
+ title,
59
+ size = 'large',
60
+ stayInline = false,
61
+ noMargin = false,
62
+ ellipsis = false,
63
+ iconSize,
64
+ iconSizeMobile,
65
+ ...svgImageProps
66
+ } = props
67
+ const classes = useStyles()
68
+
69
+ const variants: Record<IconHeaderSize, IconHeaderHeadings> = {
70
+ small: 'h5',
71
+ medium: 'h4',
72
+ large: 'h2',
73
+ }
74
+
75
+ const iconSizes = {
76
+ small: 32,
77
+ medium: 48,
78
+ large: 64,
79
+ }
80
+
81
+ const iconMobileSizes = {
82
+ small: 24,
83
+ medium: 32,
84
+ large: 40,
85
+ }
86
+
87
+ return (
88
+ <div className={clsx(classes.container, !noMargin && classes.margin)}>
89
+ <div className={clsx(classes.innerContainer, !stayInline && classes.breakColumnsDesktop)}>
90
+ <SvgImage
91
+ {...svgImageProps}
92
+ size={iconSize ?? iconSizes[size]}
93
+ mobileSize={iconSizeMobile ?? iconMobileSizes[size]}
94
+ loading='eager'
95
+ />
96
+ <Typography
97
+ variant={variants[size]}
98
+ component='h2'
99
+ className={clsx(
100
+ ellipsis && classes.ellipsis,
101
+ size === 'medium' && classes.mediumFontWeight,
102
+ )}
103
+ >
104
+ {title}
105
+ </Typography>
106
+ </div>
107
+ </div>
108
+ )
109
+ }
@@ -0,0 +1,18 @@
1
+ import Head from 'next/head'
2
+ import React from 'react'
3
+ import { jsonLdScriptProps } from 'react-schemaorg'
4
+ import { Thing, WithContext } from 'schema-dts'
5
+
6
+ export type JsonLdProps<T extends Thing> = {
7
+ item: WithContext<T>
8
+ }
9
+
10
+ export default function JsonLd<T extends Thing>(props: JsonLdProps<T>) {
11
+ const { item } = props
12
+
13
+ return (
14
+ <Head>
15
+ <script key='jsonld' {...jsonLdScriptProps<T>(item)} />
16
+ </Head>
17
+ )
18
+ }
package/Page/App.tsx ADDED
@@ -0,0 +1,15 @@
1
+ import { FramerNextPages } from '@graphcommerce/framer-next-pages'
2
+ import { LazyMotion } from 'framer-motion'
3
+ import { AppPropsType } from 'next/dist/shared/lib/utils'
4
+ import React, { useEffect } from 'react'
5
+ import { AppProps } from './types'
6
+
7
+ export default function App(props: AppProps & AppPropsType) {
8
+ useEffect(() => document.getElementById('jss-server-side')?.remove())
9
+
10
+ return (
11
+ <LazyMotion features={async () => (await import('./framerFeatures')).default} strict>
12
+ <FramerNextPages {...props} />
13
+ </LazyMotion>
14
+ )
15
+ }
@@ -0,0 +1,23 @@
1
+ import { ServerStyleSheets } from '@material-ui/core/styles'
2
+ import NextDocument, { DocumentContext } from 'next/document'
3
+ import React from 'react'
4
+
5
+ export default class Document extends NextDocument {
6
+ static getInitialProps = async (ctx: DocumentContext) => {
7
+ const sheets = new ServerStyleSheets()
8
+ const originalRenderPage = ctx.renderPage
9
+
10
+ ctx.renderPage = () =>
11
+ originalRenderPage({
12
+ enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
13
+ })
14
+
15
+ const initialProps = await NextDocument.getInitialProps(ctx)
16
+
17
+ return {
18
+ ...initialProps,
19
+ // Styles fragment is rendered after the app and page rendering finish.
20
+ styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,4 @@
1
+ // features.js
2
+ import { domMax } from 'framer-motion'
3
+
4
+ export default domMax
package/Page/types.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { ParsedUrlQuery } from 'querystring'
2
+ import { NormalizedCacheObject } from '@apollo/client'
3
+ import { GetStaticProps as GetStaticPropsNext } from 'next'
4
+ import { AppProps as NextAppProps } from 'next/app'
5
+
6
+ type AnyObj = Record<string, unknown>
7
+
8
+ type ApolloStateProps = { apolloState: NormalizedCacheObject }
9
+
10
+ export type GetStaticProps<
11
+ PL extends AnyObj,
12
+ P extends AnyObj = AnyObj,
13
+ Q extends ParsedUrlQuery = ParsedUrlQuery
14
+ > = GetStaticPropsNext<P & Omit<PL, 'children'> & ApolloStateProps, Q>
15
+
16
+ /** Used by _app */
17
+ export type AppProps = Omit<NextAppProps, 'pageProps'> & {
18
+ pageProps: ApolloStateProps
19
+ }
@@ -0,0 +1,46 @@
1
+ import { LinearProgress, makeStyles, Fade } from '@material-ui/core'
2
+ import zIndex from '@material-ui/core/styles/zIndex'
3
+ import { useRouter } from 'next/router'
4
+ import React, { useEffect, useState } from 'react'
5
+
6
+ const useStyles = makeStyles(
7
+ {
8
+ progress: {
9
+ position: 'fixed',
10
+ width: '100%',
11
+ top: 0,
12
+ height: 3,
13
+ marginBottom: -3,
14
+ zIndex: zIndex.tooltip,
15
+ },
16
+ },
17
+ { name: 'PageLoadIndicator' },
18
+ )
19
+
20
+ function PageLoadIndicator() {
21
+ const router = useRouter()
22
+ const classes = useStyles()
23
+ const [loading, setLoading] = useState<boolean>(false)
24
+
25
+ useEffect(() => {
26
+ const show = () => setLoading(true)
27
+ const hide = () => setLoading(false)
28
+ router.events.on('routeChangeStart', show)
29
+ router.events.on('routeChangeComplete', hide)
30
+ router.events.on('routeChangeError', hide)
31
+
32
+ return () => {
33
+ router.events.off('routeChangeStart', show)
34
+ router.events.off('routeChangeComplete', hide)
35
+ router.events.off('routeChangeError', hide)
36
+ }
37
+ }, [router.events])
38
+
39
+ return (
40
+ <Fade in={loading}>
41
+ <LinearProgress className={classes.progress} />
42
+ </Fade>
43
+ )
44
+ }
45
+
46
+ export default PageLoadIndicator