@graphcommerce/next-ui 3.18.1 → 3.20.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/AppShell/AppShellSticky/index.tsx +3 -19
- package/AppShell/DesktopNavActions.tsx +3 -4
- package/AppShell/DesktopNavBar.tsx +4 -4
- package/AppShell/FixedFab.tsx +1 -6
- package/AppShell/GlobalHead.tsx +36 -0
- package/AppShell/Logo.tsx +11 -20
- package/AppShell/MenuFab.tsx +19 -8
- package/AppShell/MenuFabSecondaryItem.tsx +3 -3
- package/AppShell/PlaceholderFab/index.tsx +8 -27
- package/AppShell/index.tsx +19 -0
- package/AppShell/useFabAnimation.tsx +3 -2
- package/AppShell/useFixedFabAnimation.tsx +5 -5
- package/Blog/BlogAuthor/index.tsx +10 -6
- package/Blog/BlogHeader/index.tsx +7 -3
- package/Blog/BlogList/index.tsx +1 -1
- package/Blog/BlogListItem/index.tsx +2 -1
- package/Blog/BlogTitle/index.tsx +5 -5
- package/Button/index.tsx +27 -22
- package/ButtonLink/index.tsx +1 -1
- package/CHANGELOG.md +73 -0
- package/ChipMenu/index.tsx +2 -2
- package/FlagAvatar/index.tsx +3 -3
- package/{AppShell/Footer/index.tsx → Footer/Footer.tsx} +4 -4
- package/{AppShell/Footer → Footer}/SocialIcon.tsx +3 -3
- package/Footer/index.ts +2 -0
- package/Form/InputCheckmark.tsx +3 -3
- package/Form/index.tsx +8 -3
- package/FramerScroller/components/SidebarGallery.tsx +25 -21
- package/FramerScroller/components/SidebarSlider.tsx +2 -6
- package/FullPageMessage/index.tsx +1 -1
- package/IconHeader/index.tsx +2 -15
- package/Layout/components/LayoutHeader.tsx +151 -0
- package/Layout/components/LayoutHeaderBack.tsx +50 -0
- package/Layout/components/LayoutHeaderClose.tsx +27 -0
- package/Layout/components/LayoutHeaderContent.tsx +173 -0
- package/Layout/components/LayoutHeadertypes.ts +10 -0
- package/Layout/components/LayoutProvider.tsx +17 -0
- package/{Title/index.tsx → Layout/components/LayoutTitle.tsx} +24 -15
- package/Layout/context/layoutContext.tsx +7 -0
- package/Layout/hooks/useScrollY.tsx +6 -0
- package/Layout/index.ts +5 -0
- package/Layout/types.ts +5 -0
- package/LayoutDefault/components/LayoutDefault.tsx +90 -0
- package/LayoutDefault/index.ts +1 -0
- package/LayoutOverlay/components/LayoutOverlay.tsx +25 -0
- package/LayoutOverlay/components/LayoutOverlayBase.tsx +354 -0
- package/LayoutOverlay/components/LayoutOverlayHeader.tsx +5 -0
- package/LayoutOverlay/hooks/useOverlayPosition.ts +70 -0
- package/LayoutOverlay/index.ts +2 -0
- package/Page/App.tsx +2 -0
- package/PageMeta/index.tsx +3 -0
- package/Pagination/index.tsx +0 -1
- package/Row/ButtonLinkList/index.tsx +1 -1
- package/Row/ColumnOneBoxed/index.tsx +1 -1
- package/Row/ColumnTwoWithTop/index.tsx +3 -3
- package/Row/ContentLinks/index.tsx +3 -3
- package/Row/HeroBanner/index.tsx +28 -21
- package/Row/IconBlocks/index.tsx +1 -1
- package/Row/ImageText/index.tsx +12 -5
- package/Row/ImageTextBoxed/index.tsx +3 -1
- package/Row/ParagraphWithSidebarSlide/index.tsx +2 -0
- package/Row/SpecialBanner/index.tsx +11 -7
- package/Row/index.tsx +1 -1
- package/Snackbar/MessageSnackbarImpl.tsx +2 -1
- package/StarRatingField/index.tsx +3 -4
- package/Stepper/Stepper.tsx +1 -1
- package/Styles/breakpointVal.tsx +22 -0
- package/Styles/classesPicker.ts +41 -0
- package/Styles/responsiveVal.tsx +1 -1
- package/SvgImage/SvgImageSimple.tsx +14 -11
- package/SvgImage/index.tsx +9 -11
- package/TextInputNumber/index.tsx +3 -4
- package/Theme/types.ts +14 -12
- package/ToggleButton/index.tsx +6 -13
- package/UspList/UspListItem.tsx +4 -2
- package/icons/index.tsx +2 -0
- package/index.ts +9 -42
- package/package.json +8 -9
- package/AppShell/AppShellHeader/appShellHeaderContext.tsx +0 -11
- package/AppShell/AppShellHeader/index.tsx +0 -438
- package/AppShell/AppShellHeader/useAppShellHeaderContext.tsx +0 -6
- package/AppShell/AppShellProvider/index.tsx +0 -18
- package/AppShell/AppShellTitle/index.tsx +0 -45
- package/AppShell/ForwardButton.tsx +0 -53
- package/AppShell/FullPageShellBase.tsx +0 -82
- package/AppShell/MinimalPageShellBase.tsx +0 -22
- package/AppShell/PageShellHeader/index.tsx +0 -14
- package/AppShell/SheetShellBase/index.tsx +0 -114
- package/AppShell/SheetShellBase/useSheetStyles.ts +0 -18
- package/AppShell/SheetShellDragIndicator/index.tsx +0 -55
- package/AppShell/SheetShellHeader/index.tsx +0 -28
- package/AppShell/ShellBase.tsx +0 -45
- package/Debug/DebugSpacer.tsx +0 -51
- package/FramerNextPagesSlider/Slide.tsx +0 -71
- package/FramerNextPagesSlider/Slider.tsx +0 -39
- package/FramerNextPagesSlider/index.ts +0 -1
- package/FramerNextPagesSlider/types.ts +0 -3
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { usePageContext, usePageRouter, useScrollOffset } from '@graphcommerce/framer-next-pages'
|
|
2
|
+
import { Scroller, useScrollerContext, useScrollTo } from '@graphcommerce/framer-scroller'
|
|
3
|
+
import { useElementScroll, useIsomorphicLayoutEffect } from '@graphcommerce/framer-utils'
|
|
4
|
+
import { makeStyles, Theme } from '@material-ui/core'
|
|
5
|
+
import {
|
|
6
|
+
m,
|
|
7
|
+
MotionValue,
|
|
8
|
+
useDomEvent,
|
|
9
|
+
useMotionValue,
|
|
10
|
+
usePresence,
|
|
11
|
+
useTransform,
|
|
12
|
+
} from 'framer-motion'
|
|
13
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
14
|
+
import LayoutProvider from '../../Layout/components/LayoutProvider'
|
|
15
|
+
import { UseStyles } from '../../Styles'
|
|
16
|
+
import { classesPicker } from '../../Styles/classesPicker'
|
|
17
|
+
import { useOverlayPosition } from '../hooks/useOverlayPosition'
|
|
18
|
+
|
|
19
|
+
const useStyles = makeStyles(
|
|
20
|
+
(theme: Theme) => ({
|
|
21
|
+
root: {
|
|
22
|
+
display: 'grid',
|
|
23
|
+
cursor: 'default',
|
|
24
|
+
overflow: 'auto',
|
|
25
|
+
height: '100vh',
|
|
26
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
27
|
+
height: '-webkit-fill-available',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
rootVariantSmLeft: {
|
|
31
|
+
[theme.breakpoints.down('sm')]: {
|
|
32
|
+
gridTemplate: `
|
|
33
|
+
"overlay beforeOverlay"
|
|
34
|
+
"afterOverlay afterOverlay"
|
|
35
|
+
`,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
rootVariantMdLeft: {
|
|
39
|
+
[theme.breakpoints.up('md')]: {
|
|
40
|
+
gridTemplate: `
|
|
41
|
+
"overlay beforeOverlay"
|
|
42
|
+
"afterOverlay afterOverlay"
|
|
43
|
+
`,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
rootVariantSmRight: {
|
|
47
|
+
[theme.breakpoints.down('sm')]: {
|
|
48
|
+
gridTemplate: `
|
|
49
|
+
"beforeOverlay overlay"
|
|
50
|
+
"afterOverlay afterOverlay"
|
|
51
|
+
`,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
rootVariantMdRight: {
|
|
55
|
+
[theme.breakpoints.up('md')]: {
|
|
56
|
+
gridTemplate: `
|
|
57
|
+
"beforeOverlay overlay"
|
|
58
|
+
"afterOverlay afterOverlay"
|
|
59
|
+
`,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
rootVariantSmBottom: {
|
|
63
|
+
[theme.breakpoints.down('sm')]: {
|
|
64
|
+
gridTemplate: `"beforeOverlay" "overlay" "afterOverlay"`,
|
|
65
|
+
height: '100vh',
|
|
66
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
67
|
+
height: '-webkit-fill-available',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
rootVariantMdBottom: {
|
|
72
|
+
[theme.breakpoints.up('md')]: {
|
|
73
|
+
gridTemplate: `"beforeOverlay" "overlay" "afterOverlay"`,
|
|
74
|
+
height: '100vh',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
beforeOverlay: {
|
|
78
|
+
gridArea: 'beforeOverlay',
|
|
79
|
+
scrollSnapAlign: 'start',
|
|
80
|
+
scrollSnapStop: 'always',
|
|
81
|
+
display: 'grid',
|
|
82
|
+
alignContent: 'end',
|
|
83
|
+
},
|
|
84
|
+
beforeOverlayVariantSmRight: {
|
|
85
|
+
[theme.breakpoints.down('sm')]: {
|
|
86
|
+
width: '100vw',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
beforeOverlayVariantMdRight: {
|
|
90
|
+
[theme.breakpoints.up('md')]: {
|
|
91
|
+
width: '100vw',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
beforeOverlayVariantSmLeft: {
|
|
96
|
+
[theme.breakpoints.down('sm')]: {
|
|
97
|
+
width: '100vw',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
beforeOverlayVariantMdLeft: {
|
|
101
|
+
[theme.breakpoints.up('md')]: {
|
|
102
|
+
width: '100vw',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
beforeOverlayVariantMdBottom: {
|
|
107
|
+
[theme.breakpoints.up('md')]: {
|
|
108
|
+
height: '100vh',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
beforeOverlayVariantSmBottom: {
|
|
112
|
+
[theme.breakpoints.down('sm')]: {
|
|
113
|
+
height: '100vh',
|
|
114
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
115
|
+
height: '-webkit-fill-available',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
overlay: {
|
|
120
|
+
pointerEvents: 'none',
|
|
121
|
+
gridArea: 'overlay',
|
|
122
|
+
scrollSnapAlign: 'start',
|
|
123
|
+
width: 'min-content',
|
|
124
|
+
minHeight: '100vh',
|
|
125
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
126
|
+
minHeight: '-webkit-fill-available',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
overlayVariantSmBottom: {
|
|
130
|
+
[theme.breakpoints.down('sm')]: {
|
|
131
|
+
marginTop: `calc(${theme.appShell.headerHeightSm} * 0.5 * -1)`,
|
|
132
|
+
paddingTop: `calc(${theme.appShell.headerHeightSm} * 0.5)`,
|
|
133
|
+
scrollSnapStop: 'always',
|
|
134
|
+
display: 'grid',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
overlayVariantMdBottom: {
|
|
138
|
+
[theme.breakpoints.up('md')]: {
|
|
139
|
+
marginTop: `calc(${theme.appShell.headerHeightMd} + (${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * 0.5)`,
|
|
140
|
+
paddingTop: `calc(${theme.appShell.headerHeightMd} + (${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * -0.5)`,
|
|
141
|
+
scrollSnapAlign: 'start',
|
|
142
|
+
scrollSnapStop: 'always',
|
|
143
|
+
display: 'grid',
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
overlayPane: {
|
|
147
|
+
pointerEvents: 'all',
|
|
148
|
+
backgroundColor: theme.palette.background.paper,
|
|
149
|
+
boxShadow: theme.shadows[24],
|
|
150
|
+
minWidth: 'min(800px, 90vw)',
|
|
151
|
+
},
|
|
152
|
+
overlayPaneVariantSmBottom: {
|
|
153
|
+
[theme.breakpoints.down('sm')]: {
|
|
154
|
+
width: '100vw',
|
|
155
|
+
borderRadius: 10,
|
|
156
|
+
boxShadow: '0px 0px 20px rgba(0, 0, 0, 0.1)',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
overlayPaneVariantMdBottom: {
|
|
160
|
+
[theme.breakpoints.up('md')]: {
|
|
161
|
+
width: '100vw',
|
|
162
|
+
borderRadius: 10,
|
|
163
|
+
boxShadow: '0px 0px 20px rgba(0, 0, 0, 0.1)',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
overlayPaneVariantSmLeft: {
|
|
167
|
+
[theme.breakpoints.down('sm')]: {
|
|
168
|
+
paddingBottom: 1,
|
|
169
|
+
minHeight: '100vh',
|
|
170
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
171
|
+
minHeight: '-webkit-fill-available',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
overlayPaneVariantMdLeft: {
|
|
176
|
+
[theme.breakpoints.up('md')]: {
|
|
177
|
+
paddingBottom: 1,
|
|
178
|
+
minHeight: '100vh',
|
|
179
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
180
|
+
minHeight: '-webkit-fill-available',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
overlayPaneVariantSmRight: {
|
|
185
|
+
[theme.breakpoints.down('sm')]: {
|
|
186
|
+
paddingBottom: 1,
|
|
187
|
+
minHeight: '100vh',
|
|
188
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
189
|
+
minHeight: '-webkit-fill-available',
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
overlayPaneVariantMdRight: {
|
|
194
|
+
[theme.breakpoints.up('md')]: {
|
|
195
|
+
paddingBottom: 1,
|
|
196
|
+
minHeight: '100vh',
|
|
197
|
+
'@supports (-webkit-touch-callout: none)': {
|
|
198
|
+
minHeight: '-webkit-fill-available',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
afterOverlay: {
|
|
203
|
+
gridArea: 'afterOverlay',
|
|
204
|
+
scrollSnapAlign: 'start',
|
|
205
|
+
},
|
|
206
|
+
backdrop: {
|
|
207
|
+
zIndex: -1,
|
|
208
|
+
position: 'fixed',
|
|
209
|
+
display: 'flex',
|
|
210
|
+
alignItems: 'center',
|
|
211
|
+
justifyContent: 'center',
|
|
212
|
+
right: 0,
|
|
213
|
+
bottom: 0,
|
|
214
|
+
top: 0,
|
|
215
|
+
left: 0,
|
|
216
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
217
|
+
WebkitTapHighlightColor: 'transparent',
|
|
218
|
+
},
|
|
219
|
+
}),
|
|
220
|
+
{ name: 'Overlay' },
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
export type LayoutOverlayVariant = 'left' | 'bottom' | 'right'
|
|
224
|
+
|
|
225
|
+
export type LayoutOverlayBaseProps = {
|
|
226
|
+
children?: React.ReactNode
|
|
227
|
+
variantSm: LayoutOverlayVariant
|
|
228
|
+
variantMd: LayoutOverlayVariant
|
|
229
|
+
} & UseStyles<typeof useStyles>
|
|
230
|
+
|
|
231
|
+
export enum OverlayPosition {
|
|
232
|
+
UNOPENED = -1,
|
|
233
|
+
OPENED = 1,
|
|
234
|
+
CLOSED = 0,
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function LayoutOverlayBase(props: LayoutOverlayBaseProps) {
|
|
238
|
+
const { children, variantSm, variantMd } = props
|
|
239
|
+
const { scrollerRef } = useScrollerContext()
|
|
240
|
+
const positions = useOverlayPosition()
|
|
241
|
+
const scrollTo = useScrollTo()
|
|
242
|
+
const [isPresent, safeToRemove] = usePresence()
|
|
243
|
+
|
|
244
|
+
const { closeSteps, active, direction } = usePageContext()
|
|
245
|
+
const pageRouter = usePageRouter()
|
|
246
|
+
|
|
247
|
+
const position = useMotionValue<OverlayPosition>(OverlayPosition.UNOPENED)
|
|
248
|
+
|
|
249
|
+
const classes = useStyles(props)
|
|
250
|
+
const className = classesPicker(classes, { variantSm, variantMd })
|
|
251
|
+
|
|
252
|
+
const overlayRef = useRef<HTMLDivElement>(null)
|
|
253
|
+
|
|
254
|
+
const scroll = useElementScroll(scrollerRef)
|
|
255
|
+
|
|
256
|
+
useIsomorphicLayoutEffect(() => {
|
|
257
|
+
const scroller = scrollerRef.current
|
|
258
|
+
if (!scroller || !isPresent) return
|
|
259
|
+
|
|
260
|
+
const open = { x: positions.open.x.get(), y: positions.open.y.get() }
|
|
261
|
+
|
|
262
|
+
if (direction === 1 && position.get() !== OverlayPosition.OPENED) {
|
|
263
|
+
scroller.scrollLeft = positions.closed.x.get()
|
|
264
|
+
scroller.scrollTop = positions.closed.y.get()
|
|
265
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
266
|
+
scrollTo(open).then(() => {
|
|
267
|
+
scroller.scrollLeft = positions.open.x.get()
|
|
268
|
+
scroller.scrollTop = positions.open.y.get()
|
|
269
|
+
position.set(OverlayPosition.OPENED)
|
|
270
|
+
})
|
|
271
|
+
} else {
|
|
272
|
+
scroller.scrollLeft = open.x
|
|
273
|
+
scroller.scrollTop = open.y
|
|
274
|
+
}
|
|
275
|
+
}, [direction, isPresent, position, positions, scrollTo, scrollerRef])
|
|
276
|
+
|
|
277
|
+
// Make sure the overlay stays open when resizing the window.
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
const scroller = scrollerRef.current
|
|
280
|
+
if (!scroller) return () => {}
|
|
281
|
+
|
|
282
|
+
const resize = () => {
|
|
283
|
+
if (positions.open.visible.get() !== 1) return
|
|
284
|
+
scroller.scrollLeft = positions.open.x.get()
|
|
285
|
+
scroller.scrollTop = positions.open.y.get()
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
window.addEventListener('resize', resize)
|
|
289
|
+
return () => window.removeEventListener('resize', resize)
|
|
290
|
+
// We're not checking for all deps, because that will cause rerenders.
|
|
291
|
+
// The scroller context shouldn't be changing, but at the moment it is.
|
|
292
|
+
}, [positions, scrollerRef])
|
|
293
|
+
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
if (isPresent) return
|
|
296
|
+
|
|
297
|
+
position.set(OverlayPosition.CLOSED)
|
|
298
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
299
|
+
scrollTo({
|
|
300
|
+
x: positions.closed.x.get(),
|
|
301
|
+
y: positions.closed.y.get(),
|
|
302
|
+
}).then(() => safeToRemove?.())
|
|
303
|
+
}, [isPresent, position, positions, safeToRemove, scrollTo])
|
|
304
|
+
|
|
305
|
+
// Only go back to a previous page if the overlay isn't closed.
|
|
306
|
+
const closeOverlay = useCallback(() => {
|
|
307
|
+
if (position.get() !== OverlayPosition.OPENED) return
|
|
308
|
+
position.set(OverlayPosition.CLOSED)
|
|
309
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
310
|
+
pageRouter.go(closeSteps * -1)
|
|
311
|
+
}, [closeSteps, pageRouter, position])
|
|
312
|
+
|
|
313
|
+
// Handle escape key
|
|
314
|
+
const windowRef = useRef(typeof window !== 'undefined' ? window : null)
|
|
315
|
+
const handleEscape = (e: KeyboardEvent | Event) => {
|
|
316
|
+
if (active && (e as KeyboardEvent)?.key === 'Escape') closeOverlay()
|
|
317
|
+
}
|
|
318
|
+
useDomEvent(windowRef, 'keyup', handleEscape, { passive: true })
|
|
319
|
+
|
|
320
|
+
// When the overlay isn't visible anymore, we navigate back.
|
|
321
|
+
useEffect(() => positions.open.visible.onChange((o) => o === 0 && closeOverlay()))
|
|
322
|
+
|
|
323
|
+
// Measure the offset of the overlay in the scroller.
|
|
324
|
+
|
|
325
|
+
const offsetY = useMotionValue(0)
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
if (!overlayRef.current) return () => {}
|
|
328
|
+
const ro = new ResizeObserver(([entry]) => offsetY.set(entry.contentRect.top))
|
|
329
|
+
ro.observe(overlayRef.current)
|
|
330
|
+
return () => ro.disconnect()
|
|
331
|
+
}, [offsetY])
|
|
332
|
+
|
|
333
|
+
// Create the exact position for the LayoutProvider which offsets the top of the overlay
|
|
334
|
+
const offsetPageY = useScrollOffset().y
|
|
335
|
+
const scrollWithoffset = useTransform(
|
|
336
|
+
[scroll.y, positions.open.y, offsetY] as MotionValue<number | string>[],
|
|
337
|
+
([y, openY, offsetYv]: number[]) => Math.max(0, y - openY - offsetYv + offsetPageY),
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
return (
|
|
341
|
+
<>
|
|
342
|
+
<m.div {...className('backdrop')} style={{ opacity: positions.open.visible }} />
|
|
343
|
+
<Scroller {...className('root')} grid={false} hideScrollbar>
|
|
344
|
+
<div {...className('beforeOverlay')} onClick={closeOverlay} />
|
|
345
|
+
<div {...className('overlay')} ref={overlayRef}>
|
|
346
|
+
<div {...className('overlayPane')}>
|
|
347
|
+
<LayoutProvider scroll={scrollWithoffset}>{children}</LayoutProvider>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
<div {...className('afterOverlay')} />
|
|
351
|
+
</Scroller>
|
|
352
|
+
</>
|
|
353
|
+
)
|
|
354
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useScrollerContext } from '@graphcommerce/framer-scroller'
|
|
2
|
+
import {
|
|
3
|
+
useConstant,
|
|
4
|
+
useElementScroll,
|
|
5
|
+
useIsomorphicLayoutEffect,
|
|
6
|
+
} from '@graphcommerce/framer-utils'
|
|
7
|
+
import { motionValue } from 'framer-motion'
|
|
8
|
+
import { useEffect } from 'react'
|
|
9
|
+
|
|
10
|
+
export function useOverlayPosition() {
|
|
11
|
+
const { getScrollSnapPositions, scrollerRef } = useScrollerContext()
|
|
12
|
+
|
|
13
|
+
const state = useConstant(() => ({
|
|
14
|
+
open: {
|
|
15
|
+
x: motionValue(0),
|
|
16
|
+
y: motionValue(0),
|
|
17
|
+
visible: motionValue(0),
|
|
18
|
+
},
|
|
19
|
+
closed: { x: motionValue(0), y: motionValue(0) },
|
|
20
|
+
}))
|
|
21
|
+
|
|
22
|
+
const scroll = useElementScroll(scrollerRef)
|
|
23
|
+
|
|
24
|
+
useIsomorphicLayoutEffect(() => {
|
|
25
|
+
if (!scrollerRef.current) return () => {}
|
|
26
|
+
|
|
27
|
+
const measure = () => {
|
|
28
|
+
const positions = getScrollSnapPositions()
|
|
29
|
+
state.open.x.set(positions.x[1])
|
|
30
|
+
state.closed.x.set(positions.x[0])
|
|
31
|
+
state.open.y.set(positions.y[1])
|
|
32
|
+
state.closed.y.set(positions.y[0])
|
|
33
|
+
}
|
|
34
|
+
const ro = new ResizeObserver(measure)
|
|
35
|
+
measure()
|
|
36
|
+
|
|
37
|
+
ro.observe(scrollerRef.current)
|
|
38
|
+
return () => ro.disconnect()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// sets a float between 0 and 1 for the visibility of the overlay
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const calc = () => {
|
|
44
|
+
const x = scroll.x.get()
|
|
45
|
+
const y = scroll.y.get()
|
|
46
|
+
|
|
47
|
+
const yC = state.closed.y.get()
|
|
48
|
+
const yO = state.open.y.get()
|
|
49
|
+
const visY = yC === yO ? 1 : Math.max(0, Math.min(1, (y - yC) / (yO - yC)))
|
|
50
|
+
|
|
51
|
+
const xC = state.closed.x.get()
|
|
52
|
+
const xO = state.open.x.get()
|
|
53
|
+
const visX = xO === xC ? 1 : Math.max(0, Math.min(1, (x - xC) / (xO - xC)))
|
|
54
|
+
|
|
55
|
+
// todo: visibility sometimes flickers
|
|
56
|
+
state.open.visible.set(visY * visX)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const cancelY = scroll.y.onChange(calc)
|
|
60
|
+
const cancelX = scroll.x.onChange(calc)
|
|
61
|
+
calc()
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
cancelY()
|
|
65
|
+
cancelX()
|
|
66
|
+
}
|
|
67
|
+
}, [state, scroll])
|
|
68
|
+
|
|
69
|
+
return state
|
|
70
|
+
}
|
package/Page/App.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { FramerNextPages } from '@graphcommerce/framer-next-pages'
|
|
|
2
2
|
import { LazyMotion } from 'framer-motion'
|
|
3
3
|
import { AppPropsType } from 'next/dist/shared/lib/utils'
|
|
4
4
|
import React, { useEffect } from 'react'
|
|
5
|
+
import PageLoadIndicator from '../PageLoadIndicator'
|
|
5
6
|
import { AppProps } from './types'
|
|
6
7
|
|
|
7
8
|
export default function App(props: AppProps & AppPropsType) {
|
|
@@ -9,6 +10,7 @@ export default function App(props: AppProps & AppPropsType) {
|
|
|
9
10
|
|
|
10
11
|
return (
|
|
11
12
|
<LazyMotion features={async () => (await import('./framerFeatures')).default} strict>
|
|
13
|
+
<PageLoadIndicator />
|
|
12
14
|
<FramerNextPages {...props} />
|
|
13
15
|
</LazyMotion>
|
|
14
16
|
)
|
package/PageMeta/index.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { usePageContext } from '@graphcommerce/framer-next-pages'
|
|
1
2
|
import Head from 'next/head'
|
|
2
3
|
|
|
3
4
|
// https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#directives
|
|
@@ -22,11 +23,13 @@ export type PageMetaProps = {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export default function PageMeta(props: PageMetaProps) {
|
|
26
|
+
const { active } = usePageContext()
|
|
25
27
|
const { title, canonical, metaDescription, metaRobots = ['all'] } = props
|
|
26
28
|
|
|
27
29
|
if (!(canonical ?? 'http').startsWith('http'))
|
|
28
30
|
throw new Error(`canonical must start with http:// or https://, '${canonical}' given`)
|
|
29
31
|
|
|
32
|
+
if (!active) return null
|
|
30
33
|
return (
|
|
31
34
|
<Head>
|
|
32
35
|
<title>{title.trim()}</title>
|
package/Pagination/index.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Fab, makeStyles, Theme, Typography } from '@material-ui/core'
|
|
2
2
|
import { PaginationProps, usePagination } from '@material-ui/lab'
|
|
3
|
-
import clsx from 'clsx'
|
|
4
3
|
import React from 'react'
|
|
5
4
|
import { UseStyles } from '../Styles'
|
|
6
5
|
import SvgImageSimple from '../SvgImage/SvgImageSimple'
|
|
@@ -3,7 +3,7 @@ import React from 'react'
|
|
|
3
3
|
import Row from '..'
|
|
4
4
|
import SectionContainer from '../../SectionContainer'
|
|
5
5
|
import { UseStyles } from '../../Styles'
|
|
6
|
-
import responsiveVal from '../../Styles/responsiveVal'
|
|
6
|
+
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
7
7
|
|
|
8
8
|
const useStyles = makeStyles(
|
|
9
9
|
(theme: Theme) => ({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import React
|
|
1
|
+
import { makeStyles, Theme } from '@material-ui/core'
|
|
2
|
+
import React from 'react'
|
|
3
3
|
import Row from '..'
|
|
4
4
|
import { UseStyles } from '../../Styles'
|
|
5
5
|
|
|
@@ -19,7 +19,7 @@ const useStyles = makeStyles(
|
|
|
19
19
|
"left right"
|
|
20
20
|
`,
|
|
21
21
|
gridTemplateColumns: '1fr auto',
|
|
22
|
-
gap: `${theme.spacings.sm} ${theme.spacings.
|
|
22
|
+
gap: `${theme.spacings.sm} ${theme.spacings.xxl}`,
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
top: {
|
|
@@ -5,12 +5,12 @@ import { UseStyles } from '../../Styles'
|
|
|
5
5
|
|
|
6
6
|
const useStyles = makeStyles(
|
|
7
7
|
(theme: Theme) => ({
|
|
8
|
-
root: {
|
|
9
|
-
scroller: {
|
|
8
|
+
root: {
|
|
10
9
|
marginBottom: `${theme.spacings.lg}`,
|
|
10
|
+
},
|
|
11
|
+
scroller: {
|
|
11
12
|
justifyContent: 'start',
|
|
12
13
|
gap: `${theme.spacings.md}`,
|
|
13
|
-
alignContent: 'center',
|
|
14
14
|
gridAutoColumns: `max-content`,
|
|
15
15
|
},
|
|
16
16
|
title: {
|
package/Row/HeroBanner/index.tsx
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { ContainerProps, Theme, makeStyles } from '@material-ui/core'
|
|
2
|
-
import { m, useTransform
|
|
1
|
+
import { ContainerProps, Theme, makeStyles, useTheme, useMediaQuery } from '@material-ui/core'
|
|
2
|
+
import { m, useTransform } from 'framer-motion'
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import Row from '..'
|
|
5
|
+
import { useScrollY } from '../../Layout/hooks/useScrollY'
|
|
5
6
|
import { UseStyles } from '../../Styles'
|
|
6
|
-
import responsiveVal from '../../Styles/responsiveVal'
|
|
7
|
+
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
7
8
|
|
|
8
9
|
const useStyles = makeStyles(
|
|
9
10
|
(theme: Theme) => ({
|
|
10
|
-
container: {
|
|
11
|
-
paddingLeft: 0,
|
|
12
|
-
paddingRight: 0,
|
|
13
|
-
},
|
|
14
11
|
wrapper: {
|
|
15
12
|
position: 'relative',
|
|
16
13
|
},
|
|
@@ -22,22 +19,19 @@ const useStyles = makeStyles(
|
|
|
22
19
|
justifyItems: 'center',
|
|
23
20
|
alignContent: 'center',
|
|
24
21
|
padding: `${theme.spacings.lg} ${theme.spacings.md}`,
|
|
25
|
-
|
|
22
|
+
paddingTop: `calc(${theme.spacings.lg} - ${theme.spacings.md})`,
|
|
23
|
+
minHeight: `calc(100vh - ${theme.appShell.headerHeightSm})`,
|
|
26
24
|
'& > *': {
|
|
27
25
|
zIndex: 1,
|
|
28
26
|
maxWidth: 'max-content',
|
|
29
27
|
},
|
|
30
|
-
[theme.breakpoints.down('sm')]: {
|
|
31
|
-
['@supports (-webkit-touch-callout: none)']: {
|
|
32
|
-
minHeight: '-webkit-fill-available',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
28
|
[theme.breakpoints.up('md')]: {
|
|
36
29
|
width: '70%',
|
|
37
|
-
minHeight: `calc(100vh - ${theme.
|
|
30
|
+
minHeight: `calc(100vh - ${theme.appShell.headerHeightMd})`,
|
|
38
31
|
},
|
|
39
32
|
[theme.breakpoints.up('lg')]: {
|
|
40
33
|
padding: `${theme.spacings.lg} ${theme.spacings.lg}`,
|
|
34
|
+
paddingTop: `calc(${theme.spacings.lg} - ${theme.spacings.md})`,
|
|
41
35
|
width: '50%',
|
|
42
36
|
},
|
|
43
37
|
},
|
|
@@ -50,16 +44,23 @@ const useStyles = makeStyles(
|
|
|
50
44
|
display: 'grid',
|
|
51
45
|
justifyItems: 'center',
|
|
52
46
|
overflow: 'hidden',
|
|
53
|
-
paddingBottom: theme.
|
|
47
|
+
paddingBottom: theme.page.horizontal,
|
|
54
48
|
'& video': {
|
|
55
49
|
objectFit: 'cover',
|
|
56
50
|
width: '100%',
|
|
57
51
|
height: '100%',
|
|
52
|
+
[theme.breakpoints.down('sm')]: {
|
|
53
|
+
borderRadius: responsiveVal(theme.shape.borderRadius * 2, theme.shape.borderRadius * 3),
|
|
54
|
+
},
|
|
58
55
|
},
|
|
59
56
|
[theme.breakpoints.up('md')]: {
|
|
60
57
|
height: '100%',
|
|
61
58
|
},
|
|
62
59
|
},
|
|
60
|
+
animated: {
|
|
61
|
+
borderRadius: responsiveVal(theme.shape.borderRadius * 2, theme.shape.borderRadius * 3),
|
|
62
|
+
overflow: 'hidden',
|
|
63
|
+
},
|
|
63
64
|
}),
|
|
64
65
|
{ name: 'HeroBanner' },
|
|
65
66
|
)
|
|
@@ -74,23 +75,29 @@ export type HeroBannerProps = UseStyles<typeof useStyles> &
|
|
|
74
75
|
export default function HeroBanner(props: HeroBannerProps) {
|
|
75
76
|
const { pageLinks, videoSrc, children, ...containerProps } = props
|
|
76
77
|
const classes = useStyles(props)
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
const
|
|
78
|
+
const theme = useTheme()
|
|
79
|
+
const scrollY = useScrollY()
|
|
80
|
+
const width = useTransform(
|
|
81
|
+
scrollY,
|
|
82
|
+
[10, 150],
|
|
83
|
+
[`calc(100% - ${responsiveVal(20, 60)}))`, `calc(100% - ${responsiveVal(0, 0)})`],
|
|
84
|
+
)
|
|
85
|
+
const matches = useMediaQuery(theme.breakpoints.down('sm'))
|
|
86
|
+
const borderRadius = useTransform(
|
|
80
87
|
scrollY,
|
|
81
88
|
[10, 150],
|
|
82
|
-
[
|
|
89
|
+
[`${responsiveVal(8, 12)}`, `${responsiveVal(0, 0)}`],
|
|
83
90
|
)
|
|
84
91
|
|
|
85
92
|
return (
|
|
86
|
-
<Row maxWidth={false} {...containerProps}
|
|
93
|
+
<Row maxWidth={false} {...containerProps} disableGutters>
|
|
87
94
|
<div className={classes.wrapper}>
|
|
88
95
|
<div className={classes.copy}>
|
|
89
96
|
{children}
|
|
90
97
|
{pageLinks}
|
|
91
98
|
</div>
|
|
92
99
|
<div className={classes.asset}>
|
|
93
|
-
<m.div style={{ width:
|
|
100
|
+
<m.div style={{ width: !matches ? width : 0, borderRadius }} className={classes.animated}>
|
|
94
101
|
<video src={videoSrc} autoPlay muted loop playsInline disableRemotePlayback />
|
|
95
102
|
</m.div>
|
|
96
103
|
</div>
|
package/Row/IconBlocks/index.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { makeStyles, Theme, Typography } from '@material-ui/core'
|
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import Row from '..'
|
|
4
4
|
import { UseStyles } from '../../Styles'
|
|
5
|
-
import responsiveVal from '../../Styles/responsiveVal'
|
|
5
|
+
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
6
6
|
|
|
7
7
|
const useStyles = makeStyles(
|
|
8
8
|
(theme: Theme) => ({
|