@graphcommerce/next-ui 3.19.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 +2 -3
- package/AppShell/FixedFab.tsx +1 -6
- package/AppShell/GlobalHead.tsx +36 -0
- package/AppShell/Logo.tsx +11 -20
- package/AppShell/MenuFab.tsx +5 -3
- 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 +3 -2
- package/Blog/BlogAuthor/index.tsx +1 -1
- package/Blog/BlogHeader/index.tsx +2 -3
- package/Blog/BlogList/index.tsx +1 -1
- package/Blog/BlogListItem/index.tsx +1 -1
- package/Blog/BlogTitle/index.tsx +5 -5
- package/Button/index.tsx +26 -21
- package/CHANGELOG.md +30 -0
- package/ChipMenu/index.tsx +2 -2
- package/FlagAvatar/index.tsx +3 -3
- package/{AppShell/Footer/index.tsx → Footer/Footer.tsx} +3 -3
- package/{AppShell/Footer → Footer}/SocialIcon.tsx +3 -3
- package/Footer/index.ts +2 -0
- package/Form/InputCheckmark.tsx +3 -3
- package/Form/index.tsx +1 -1
- package/FramerScroller/components/SidebarGallery.tsx +24 -20
- 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 +7 -11
- package/Row/IconBlocks/index.tsx +1 -1
- package/Row/ImageText/index.tsx +1 -1
- package/Row/ImageTextBoxed/index.tsx +1 -1
- package/Row/ParagraphWithSidebarSlide/index.tsx +1 -1
- package/Row/SpecialBanner/index.tsx +4 -3
- package/Row/index.tsx +1 -1
- package/Snackbar/MessageSnackbarImpl.tsx +1 -1
- package/StarRatingField/index.tsx +3 -4
- package/Stepper/Stepper.tsx +1 -1
- package/Styles/breakpointVal.tsx +2 -2
- package/Styles/classesPicker.ts +41 -0
- package/Styles/responsiveVal.tsx +1 -1
- package/SvgImage/SvgImageSimple.tsx +9 -8
- package/SvgImage/index.tsx +9 -11
- package/TextInputNumber/index.tsx +3 -4
- package/Theme/types.ts +14 -12
- package/ToggleButton/index.tsx +2 -4
- package/UspList/UspListItem.tsx +1 -1
- package/index.ts +9 -43
- package/package.json +8 -9
- package/AppShell/AppShellHeader/appShellHeaderContext.tsx +0 -11
- package/AppShell/AppShellHeader/index.tsx +0 -439
- 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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { usePageRouter } from '@graphcommerce/framer-next-pages'
|
|
1
2
|
import { usePrevPageRouter } from '@graphcommerce/framer-next-pages/hooks/usePrevPageRouter'
|
|
2
3
|
import {
|
|
3
4
|
CenterSlide,
|
|
@@ -12,11 +13,11 @@ import { clientSize, useMotionValueValue } from '@graphcommerce/framer-utils'
|
|
|
12
13
|
import { Fab, makeStyles, Theme, useTheme, alpha } from '@material-ui/core'
|
|
13
14
|
import clsx from 'clsx'
|
|
14
15
|
import { m, useDomEvent, useMotionValue } from 'framer-motion'
|
|
15
|
-
import { useRouter } from 'next/router'
|
|
16
16
|
import React, { useEffect, useRef } from 'react'
|
|
17
|
-
import {
|
|
17
|
+
import { classesPicker } from '../..'
|
|
18
|
+
import Row from '../../Row'
|
|
18
19
|
import { UseStyles } from '../../Styles'
|
|
19
|
-
import responsiveVal from '../../Styles/responsiveVal'
|
|
20
|
+
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
20
21
|
import SvgImageSimple from '../../SvgImage/SvgImageSimple'
|
|
21
22
|
import { iconChevronLeft, iconChevronRight, iconFullscreen, iconFullscreenExit } from '../../icons'
|
|
22
23
|
|
|
@@ -42,14 +43,14 @@ const useStyles = makeStyles(
|
|
|
42
43
|
rootZoomed: {
|
|
43
44
|
position: 'relative',
|
|
44
45
|
zIndex: theme.zIndex.modal,
|
|
45
|
-
marginTop: `calc(${theme.
|
|
46
|
+
marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
|
|
46
47
|
[theme.breakpoints.up('md')]: {
|
|
47
|
-
marginTop: `calc(${theme.
|
|
48
|
+
marginTop: `calc(${theme.appShell.headerHeightMd} * -1 - ${theme.spacings.lg})`,
|
|
48
49
|
},
|
|
49
50
|
paddingRight: 0,
|
|
50
51
|
},
|
|
51
52
|
scrollerContainer: ({ aspectRatio: [width, height] }: StyleProps) => {
|
|
52
|
-
const headerHeight = `${theme.
|
|
53
|
+
const headerHeight = `${theme.appShell.headerHeightSm} - ${theme.spacings.sm} * 2`
|
|
53
54
|
const galleryMargin = theme.spacings.lg
|
|
54
55
|
const extraSpacing = theme.spacings.md
|
|
55
56
|
|
|
@@ -172,11 +173,18 @@ export type SidebarGalleryProps = {
|
|
|
172
173
|
} & UseStyles<typeof useStyles>
|
|
173
174
|
|
|
174
175
|
export default function SidebarGallery(props: SidebarGalleryProps) {
|
|
175
|
-
const {
|
|
176
|
-
|
|
176
|
+
const {
|
|
177
|
+
sidebar,
|
|
178
|
+
images,
|
|
179
|
+
aspectRatio = [1, 1],
|
|
180
|
+
routeHash = 'gallery',
|
|
181
|
+
classes: classesBase,
|
|
182
|
+
} = props
|
|
183
|
+
|
|
184
|
+
const router = usePageRouter()
|
|
177
185
|
const prevRoute = usePrevPageRouter()
|
|
178
186
|
const clientHeight = useMotionValueValue(clientSize.y, (y) => y)
|
|
179
|
-
const classes = useStyles({ clientHeight, aspectRatio, classes:
|
|
187
|
+
const classes = useStyles({ clientHeight, aspectRatio, classes: classesBase })
|
|
180
188
|
|
|
181
189
|
const route = `#${routeHash}`
|
|
182
190
|
// We're using the URL to manage the state of the gallery.
|
|
@@ -201,16 +209,12 @@ export default function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
201
209
|
}
|
|
202
210
|
}
|
|
203
211
|
|
|
204
|
-
const
|
|
212
|
+
const className = classesPicker(classes, { zoomed })
|
|
205
213
|
const theme = useTheme()
|
|
206
214
|
const windowRef = useRef(typeof window !== 'undefined' ? window : null)
|
|
207
215
|
|
|
208
216
|
const handleEscapeKey = (e: KeyboardEvent | Event) => {
|
|
209
|
-
if (zoomed)
|
|
210
|
-
if ((e as KeyboardEvent)?.key === 'Escape') {
|
|
211
|
-
toggle()
|
|
212
|
-
}
|
|
213
|
-
}
|
|
217
|
+
if (zoomed && (e as KeyboardEvent)?.key === 'Escape') toggle()
|
|
214
218
|
}
|
|
215
219
|
|
|
216
220
|
const dragStart = useMotionValue<number>(0)
|
|
@@ -228,16 +232,16 @@ export default function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
228
232
|
return (
|
|
229
233
|
<ScrollerProvider scrollSnapAlign='center'>
|
|
230
234
|
<Row maxWidth={false} disableGutters>
|
|
231
|
-
<m.div layout className
|
|
235
|
+
<m.div layout {...className('root')}>
|
|
232
236
|
<m.div
|
|
233
237
|
layout
|
|
234
|
-
className
|
|
238
|
+
{...className('scrollerContainer')}
|
|
235
239
|
onLayoutAnimationComplete={() => {
|
|
236
240
|
if (!zoomed) document.body.style.overflow = ''
|
|
237
241
|
}}
|
|
238
242
|
>
|
|
239
243
|
<Scroller
|
|
240
|
-
className
|
|
244
|
+
{...className('scroller')}
|
|
241
245
|
hideScrollbar
|
|
242
246
|
onMouseDown={onMouseDownScroller}
|
|
243
247
|
onMouseUp={onMouseUpScroller}
|
|
@@ -299,8 +303,8 @@ export default function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
299
303
|
</div>
|
|
300
304
|
</m.div>
|
|
301
305
|
|
|
302
|
-
<div className
|
|
303
|
-
<m.div layout className
|
|
306
|
+
<div {...className('sidebarWrapper')}>
|
|
307
|
+
<m.div layout {...className('sidebar')}>
|
|
304
308
|
{sidebar}
|
|
305
309
|
</m.div>
|
|
306
310
|
</div>
|
|
@@ -8,7 +8,7 @@ import { Theme, makeStyles } from '@material-ui/core'
|
|
|
8
8
|
import React, { ReactNode } from 'react'
|
|
9
9
|
import Row from '../../Row'
|
|
10
10
|
import { UseStyles } from '../../Styles'
|
|
11
|
-
import responsiveVal from '../../Styles/responsiveVal'
|
|
11
|
+
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
12
12
|
import SvgImageSimple from '../../SvgImage/SvgImageSimple'
|
|
13
13
|
import { iconChevronLeft, iconChevronRight } from '../../icons'
|
|
14
14
|
|
|
@@ -29,10 +29,8 @@ const useStyles = makeStyles(
|
|
|
29
29
|
minWidth: 1,
|
|
30
30
|
},
|
|
31
31
|
scroller: {
|
|
32
|
-
width: '100%',
|
|
33
32
|
gridColumnGap: theme.spacings.md,
|
|
34
33
|
gridRowGap: theme.spacings.lg,
|
|
35
|
-
alignContent: 'space-around',
|
|
36
34
|
paddingRight: theme.page.horizontal,
|
|
37
35
|
gridAutoColumns: responsiveVal(200, 400),
|
|
38
36
|
},
|
|
@@ -78,9 +76,7 @@ export default function SidebarSlider(props: SidebarSliderProps) {
|
|
|
78
76
|
</div>
|
|
79
77
|
|
|
80
78
|
<div className={classes.scrollerContainer}>
|
|
81
|
-
<Scroller className={classes.scroller}
|
|
82
|
-
{children}
|
|
83
|
-
</Scroller>
|
|
79
|
+
<Scroller className={classes.scroller}>{children}</Scroller>
|
|
84
80
|
<div className={classes.centerLeft}>
|
|
85
81
|
<ScrollerButton layout direction='left' className={classes.sliderButtons}>
|
|
86
82
|
<SvgImageSimple src={iconChevronLeft} />
|
|
@@ -2,7 +2,7 @@ import { Container, Theme, Typography } from '@material-ui/core'
|
|
|
2
2
|
import { makeStyles } from '@material-ui/styles'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
4
|
import React from 'react'
|
|
5
|
-
import responsiveVal from '../Styles/responsiveVal'
|
|
5
|
+
import { responsiveVal } from '../Styles/responsiveVal'
|
|
6
6
|
|
|
7
7
|
const useStyles = makeStyles(
|
|
8
8
|
(theme: Theme) => ({
|
package/IconHeader/index.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { makeStyles, Theme, Typography } from '@material-ui/core'
|
|
2
2
|
import clsx from 'clsx'
|
|
3
3
|
import React from 'react'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import SvgImage, { SvgImageProps } from '../SvgImage'
|
|
4
|
+
import { SvgImageProps } from '../SvgImage'
|
|
5
|
+
import SvgImageSimple from '../SvgImage/SvgImageSimple'
|
|
7
6
|
|
|
8
7
|
// TODO: remove all occurrences. deprecated component
|
|
9
8
|
|
|
@@ -73,18 +72,6 @@ export default function IconHeader(props: IconHeaderProps) {
|
|
|
73
72
|
large: 'h2',
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
const iconSizes = {
|
|
77
|
-
small: 32,
|
|
78
|
-
medium: 48,
|
|
79
|
-
large: 64,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const iconMobileSizes = {
|
|
83
|
-
small: 24,
|
|
84
|
-
medium: 32,
|
|
85
|
-
large: 40,
|
|
86
|
-
}
|
|
87
|
-
|
|
88
75
|
return (
|
|
89
76
|
<div className={clsx(classes.container, !noMargin && classes.margin)}>
|
|
90
77
|
<div className={clsx(classes.innerContainer, !stayInline && classes.breakColumnsDesktop)}>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { usePageContext } from '@graphcommerce/framer-next-pages'
|
|
2
|
+
import { makeStyles, Theme } from '@material-ui/core'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { classesPicker } from '../../Styles/classesPicker'
|
|
5
|
+
import LayoutHeaderBack, { useShowBack } from './LayoutHeaderBack'
|
|
6
|
+
import LayoutHeaderClose, { useShowClose } from './LayoutHeaderClose'
|
|
7
|
+
import LayoutHeaderContent, { ContentProps } from './LayoutHeaderContent'
|
|
8
|
+
import { FloatingProps } from './LayoutHeadertypes'
|
|
9
|
+
|
|
10
|
+
export type LayoutHeaderProps = FloatingProps &
|
|
11
|
+
Omit<ContentProps, 'leftAction' | 'rightAction'> & {
|
|
12
|
+
/**
|
|
13
|
+
* Button to display on the left side of the title
|
|
14
|
+
*
|
|
15
|
+
* - Assumes it can float on desktop
|
|
16
|
+
* - Assumes it can not float on mobile
|
|
17
|
+
*/
|
|
18
|
+
primary?: React.ReactNode
|
|
19
|
+
/**
|
|
20
|
+
* Button to display on the right side of the title
|
|
21
|
+
*
|
|
22
|
+
* - Assumes it can float on desktop
|
|
23
|
+
* - Assumes it can not float on mobile
|
|
24
|
+
*/
|
|
25
|
+
secondary?: React.ReactNode
|
|
26
|
+
|
|
27
|
+
additional?: React.ReactNode
|
|
28
|
+
|
|
29
|
+
noAlign?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const useStyles = makeStyles(
|
|
33
|
+
(theme: Theme) => ({
|
|
34
|
+
sticky: {
|
|
35
|
+
zIndex: theme.zIndex.appBar,
|
|
36
|
+
position: 'sticky',
|
|
37
|
+
pointerEvents: 'none',
|
|
38
|
+
|
|
39
|
+
[theme.breakpoints.up('md')]: {
|
|
40
|
+
top: 0,
|
|
41
|
+
height: theme.appShell.appBarHeightMd,
|
|
42
|
+
marginTop: `calc((${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * -0.5)`,
|
|
43
|
+
marginBottom: `calc(${theme.appShell.appBarHeightMd} * -1 - calc((${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * -0.5))`,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
stickyNoChildren: {
|
|
47
|
+
zIndex: theme.zIndex.appBar - 2,
|
|
48
|
+
},
|
|
49
|
+
stickyVisibleSm: {
|
|
50
|
+
[theme.breakpoints.down('sm')]: {
|
|
51
|
+
top: 0,
|
|
52
|
+
marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
|
|
53
|
+
height: theme.appShell.headerHeightSm,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
stickyFloatingSm: {
|
|
57
|
+
[theme.breakpoints.down('sm')]: {
|
|
58
|
+
top: 0,
|
|
59
|
+
marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
|
|
60
|
+
height: theme.appShell.headerHeightSm,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
stickyFloatingMd: {
|
|
64
|
+
[theme.breakpoints.up('md')]: {
|
|
65
|
+
top: `calc(${theme.appShell.headerHeightMd} + calc((${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * -0.5))`,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
stickyNoAlign: {
|
|
69
|
+
[theme.breakpoints.down('sm')]: {
|
|
70
|
+
position: 'sticky',
|
|
71
|
+
left: 0,
|
|
72
|
+
right: 0,
|
|
73
|
+
top: 0,
|
|
74
|
+
marginTop: 0,
|
|
75
|
+
height: theme.appShell.headerHeightSm,
|
|
76
|
+
marginBottom: `calc(${theme.appShell.headerHeightSm} * -1)`,
|
|
77
|
+
},
|
|
78
|
+
[theme.breakpoints.up('md')]: {
|
|
79
|
+
position: 'sticky',
|
|
80
|
+
left: 0,
|
|
81
|
+
right: 0,
|
|
82
|
+
top: 0,
|
|
83
|
+
marginTop: 0,
|
|
84
|
+
height: theme.appShell.appBarHeightMd,
|
|
85
|
+
marginBottom: `calc(${theme.appShell.appBarHeightMd} * -1)`,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
stickyDivider: {
|
|
89
|
+
[theme.breakpoints.down('sm')]: {
|
|
90
|
+
marginBottom: 0,
|
|
91
|
+
},
|
|
92
|
+
[theme.breakpoints.up('md')]: {
|
|
93
|
+
marginBottom: 0,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
{ name: 'LayoutHeader' },
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
export function LayoutHeader(props: LayoutHeaderProps) {
|
|
101
|
+
const { children, additional, divider, primary, secondary, noAlign } = props
|
|
102
|
+
const classes = useStyles(props)
|
|
103
|
+
const showBack = useShowBack()
|
|
104
|
+
const showClose = useShowClose()
|
|
105
|
+
|
|
106
|
+
const floatFallback = !children
|
|
107
|
+
let { floatingSm = false, floatingMd = floatFallback } = props
|
|
108
|
+
|
|
109
|
+
if (divider) floatingMd = false
|
|
110
|
+
|
|
111
|
+
// When the primary or secondary is set, the header can't float on mobile even if the prop is passed.
|
|
112
|
+
if (divider || primary || secondary) floatingSm = false
|
|
113
|
+
|
|
114
|
+
const close = showClose && <LayoutHeaderClose />
|
|
115
|
+
const back = showBack && <LayoutHeaderBack variant={floatingSm ? 'pill' : 'pill-link'} />
|
|
116
|
+
|
|
117
|
+
let left = secondary
|
|
118
|
+
let right = primary
|
|
119
|
+
|
|
120
|
+
if (back) left = back
|
|
121
|
+
|
|
122
|
+
if (!left) left = close
|
|
123
|
+
else if (!right) right = close
|
|
124
|
+
|
|
125
|
+
if (!left && !right && !children) return null
|
|
126
|
+
|
|
127
|
+
const className = classesPicker(classes, {
|
|
128
|
+
floatingSm,
|
|
129
|
+
floatingMd,
|
|
130
|
+
visibleSm: !floatingSm,
|
|
131
|
+
visibleMd: !floatingMd,
|
|
132
|
+
noChildren: !children,
|
|
133
|
+
noAlign,
|
|
134
|
+
divider: !!divider,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<div {...className('sticky')}>
|
|
139
|
+
<LayoutHeaderContent
|
|
140
|
+
left={left}
|
|
141
|
+
right={right}
|
|
142
|
+
divider={divider}
|
|
143
|
+
floatingMd={floatingMd}
|
|
144
|
+
floatingSm={floatingSm}
|
|
145
|
+
>
|
|
146
|
+
{children}
|
|
147
|
+
{additional}
|
|
148
|
+
</LayoutHeaderContent>
|
|
149
|
+
</div>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { usePageRouter, useUp, usePrevUp, usePageContext } from '@graphcommerce/framer-next-pages'
|
|
2
|
+
import { Trans } from '@lingui/macro'
|
|
3
|
+
import PageLink from 'next/link'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import Button, { ButtonProps } from '../../Button'
|
|
6
|
+
import SvgImageSimple from '../../SvgImage/SvgImageSimple'
|
|
7
|
+
import { iconChevronLeft } from '../../icons'
|
|
8
|
+
|
|
9
|
+
export type BackProps = Omit<ButtonProps, 'onClick' | 'children'>
|
|
10
|
+
|
|
11
|
+
export function useShowBack() {
|
|
12
|
+
const router = usePageRouter()
|
|
13
|
+
const up = useUp()
|
|
14
|
+
const prevUp = usePrevUp()
|
|
15
|
+
const { backSteps } = usePageContext()
|
|
16
|
+
|
|
17
|
+
const canClickBack = backSteps > 0 && router.asPath !== prevUp?.href
|
|
18
|
+
|
|
19
|
+
if (canClickBack) return true
|
|
20
|
+
if (up?.href && up.href !== router.asPath) return true
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function LayoutHeaderBack(props: BackProps) {
|
|
25
|
+
const router = usePageRouter()
|
|
26
|
+
const up = useUp()
|
|
27
|
+
const prevUp = usePrevUp()
|
|
28
|
+
const { backSteps } = usePageContext()
|
|
29
|
+
|
|
30
|
+
const backIcon = <SvgImageSimple src={iconChevronLeft} />
|
|
31
|
+
const canClickBack = backSteps > 0 && router.asPath !== prevUp?.href
|
|
32
|
+
|
|
33
|
+
if (canClickBack)
|
|
34
|
+
return (
|
|
35
|
+
<Button onClick={() => router.back()} variant='pill-link' startIcon={backIcon} {...props}>
|
|
36
|
+
{up?.href === router.asPath ? up.title : <Trans>Back</Trans>}
|
|
37
|
+
</Button>
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if (up?.href && up.href !== router.asPath)
|
|
41
|
+
return (
|
|
42
|
+
<PageLink href={up.href} passHref>
|
|
43
|
+
<Button variant='pill-link' startIcon={backIcon} {...props}>
|
|
44
|
+
{up.title}
|
|
45
|
+
</Button>
|
|
46
|
+
</PageLink>
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { usePageRouter, usePageContext } from '@graphcommerce/framer-next-pages'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import Button from '../../Button'
|
|
4
|
+
import SvgImageSimple from '../../SvgImage/SvgImageSimple'
|
|
5
|
+
import { iconClose } from '../../icons'
|
|
6
|
+
|
|
7
|
+
export function useShowClose() {
|
|
8
|
+
const { overlayGroup } = usePageContext()
|
|
9
|
+
return !!overlayGroup
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function LayoutHeaderClose() {
|
|
13
|
+
const router = usePageRouter()
|
|
14
|
+
const { closeSteps } = usePageContext()
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Button
|
|
18
|
+
type='button'
|
|
19
|
+
onClick={() => router.go(closeSteps * -1)}
|
|
20
|
+
aria-label='Close'
|
|
21
|
+
variant='pill-link'
|
|
22
|
+
startIcon={<SvgImageSimple src={iconClose} />}
|
|
23
|
+
>
|
|
24
|
+
Close
|
|
25
|
+
</Button>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { useMotionValueValue } from '@graphcommerce/framer-utils'
|
|
2
|
+
import { Divider, makeStyles, Theme } from '@material-ui/core'
|
|
3
|
+
import React, { useRef } from 'react'
|
|
4
|
+
import { UseStyles } from '../../Styles'
|
|
5
|
+
import { classesPicker } from '../../Styles/classesPicker'
|
|
6
|
+
import { useScrollY } from '../hooks/useScrollY'
|
|
7
|
+
import { FloatingProps } from './LayoutHeadertypes'
|
|
8
|
+
|
|
9
|
+
type Classes = 'bg' | 'content' | 'left' | 'center' | 'right' | 'divider'
|
|
10
|
+
|
|
11
|
+
const time = '150ms'
|
|
12
|
+
|
|
13
|
+
const useStyles = makeStyles(
|
|
14
|
+
(theme: Theme) => ({
|
|
15
|
+
bg: {
|
|
16
|
+
position: 'absolute',
|
|
17
|
+
left: 0,
|
|
18
|
+
width: '100%',
|
|
19
|
+
backgroundColor: theme.palette.background.default,
|
|
20
|
+
opacity: 0,
|
|
21
|
+
transition: `opacity ${time}`,
|
|
22
|
+
|
|
23
|
+
height: theme.appShell.headerHeightSm,
|
|
24
|
+
[theme.breakpoints.up('md')]: {
|
|
25
|
+
height: theme.appShell.appBarHeightMd,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
bgFloatingSm: {
|
|
29
|
+
[theme.breakpoints.down('sm')]: {
|
|
30
|
+
display: 'none',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
bgFloatingMd: {
|
|
34
|
+
[theme.breakpoints.up('md')]: {
|
|
35
|
+
display: 'none',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
bgScrolled: {
|
|
39
|
+
opacity: 1,
|
|
40
|
+
},
|
|
41
|
+
content: {
|
|
42
|
+
position: 'absolute',
|
|
43
|
+
left: 0,
|
|
44
|
+
width: '100%',
|
|
45
|
+
display: 'grid',
|
|
46
|
+
gridTemplateAreas: `"left center right"`,
|
|
47
|
+
gridTemplateColumns: '1fr auto 1fr',
|
|
48
|
+
alignItems: 'center',
|
|
49
|
+
|
|
50
|
+
height: theme.appShell.headerHeightSm,
|
|
51
|
+
[theme.breakpoints.up('md')]: {
|
|
52
|
+
padding: `0 ${theme.page.horizontal}`,
|
|
53
|
+
height: theme.appShell.appBarHeightMd,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
contentFloatingMd: {
|
|
57
|
+
[theme.breakpoints.up('md')]: {
|
|
58
|
+
padding: `0 ${theme.page.horizontal}`,
|
|
59
|
+
background: 'none',
|
|
60
|
+
pointerEvents: 'none',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
contentFloatingSm: {
|
|
64
|
+
[theme.breakpoints.down('sm')]: {
|
|
65
|
+
padding: `0 ${theme.page.horizontal}`,
|
|
66
|
+
background: 'none',
|
|
67
|
+
pointerEvents: 'none',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
left: {
|
|
71
|
+
'& > *': { pointerEvents: 'all' },
|
|
72
|
+
display: 'grid',
|
|
73
|
+
gridAutoFlow: 'column',
|
|
74
|
+
gap: theme.spacings.sm,
|
|
75
|
+
gridArea: 'left',
|
|
76
|
+
justifyContent: 'start',
|
|
77
|
+
},
|
|
78
|
+
center: {
|
|
79
|
+
display: 'grid',
|
|
80
|
+
gridAutoFlow: 'column',
|
|
81
|
+
gap: theme.spacings.sm,
|
|
82
|
+
gridArea: 'center',
|
|
83
|
+
justifyContent: 'start',
|
|
84
|
+
overflow: 'hidden',
|
|
85
|
+
transition: `opacity ${time}`,
|
|
86
|
+
opacity: 0,
|
|
87
|
+
},
|
|
88
|
+
centerScrolled: {
|
|
89
|
+
opacity: 1,
|
|
90
|
+
'& > *': { pointerEvents: 'all' },
|
|
91
|
+
},
|
|
92
|
+
centerFloatingSm: {
|
|
93
|
+
[theme.breakpoints.down('sm')]: {
|
|
94
|
+
display: 'none',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
centerFloatingMd: {
|
|
98
|
+
[theme.breakpoints.up('md')]: {
|
|
99
|
+
display: 'none',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
right: {
|
|
103
|
+
'& > *': { pointerEvents: 'all' },
|
|
104
|
+
display: 'grid',
|
|
105
|
+
gridAutoFlow: 'column',
|
|
106
|
+
gap: theme.spacings.sm,
|
|
107
|
+
gridArea: 'right',
|
|
108
|
+
justifyContent: 'end',
|
|
109
|
+
},
|
|
110
|
+
divider: {
|
|
111
|
+
position: 'absolute',
|
|
112
|
+
bottom: 0,
|
|
113
|
+
left: 0,
|
|
114
|
+
right: 0,
|
|
115
|
+
transition: `opacity ${time}`,
|
|
116
|
+
opacity: 0,
|
|
117
|
+
},
|
|
118
|
+
dividerCustomDivider: {
|
|
119
|
+
opacity: 1,
|
|
120
|
+
},
|
|
121
|
+
dividerScrolled: {
|
|
122
|
+
opacity: 1,
|
|
123
|
+
},
|
|
124
|
+
dividerFloatingSm: {
|
|
125
|
+
[theme.breakpoints.down('sm')]: {
|
|
126
|
+
display: 'none',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
dividerFloatingMd: {
|
|
130
|
+
[theme.breakpoints.up('md')]: {
|
|
131
|
+
display: 'none',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
{ name: 'LayoutHeaderContent' },
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
export type ContentProps = FloatingProps &
|
|
139
|
+
UseStyles<typeof useStyles> & {
|
|
140
|
+
children?: React.ReactNode
|
|
141
|
+
left?: React.ReactNode
|
|
142
|
+
right?: React.ReactNode
|
|
143
|
+
divider?: React.ReactNode
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default function LayoutHeaderContent(props: ContentProps) {
|
|
147
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
148
|
+
const scroll = useScrollY()
|
|
149
|
+
|
|
150
|
+
const { left, children, right, divider, floatingMd = false, floatingSm = false } = props
|
|
151
|
+
const classes = useStyles(props)
|
|
152
|
+
|
|
153
|
+
const scrolled = useMotionValueValue(scroll, (y) => y > 50)
|
|
154
|
+
|
|
155
|
+
const className = classesPicker<Classes>(classes, {
|
|
156
|
+
floatingSm,
|
|
157
|
+
floatingMd,
|
|
158
|
+
scrolled,
|
|
159
|
+
customDivider: !!divider,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<>
|
|
164
|
+
<div {...className('bg')} />
|
|
165
|
+
<div {...className('content')} ref={ref}>
|
|
166
|
+
<div {...className('left')}>{left}</div>
|
|
167
|
+
<div {...className('center')}>{children}</div>
|
|
168
|
+
<div {...className('right')}>{right}</div>
|
|
169
|
+
<div {...className('divider')}>{divider ?? <Divider />}</div>
|
|
170
|
+
</div>
|
|
171
|
+
</>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Matrix<T extends string, M extends string> = `${T}${Capitalize<M>}`
|
|
2
|
+
|
|
3
|
+
// Possible configurations for the AppShellHeader
|
|
4
|
+
export type Variant = 'floating'
|
|
5
|
+
export type Size = 'sm' | 'md'
|
|
6
|
+
|
|
7
|
+
// Matrix
|
|
8
|
+
export type VariantSize = Matrix<Variant, Size>
|
|
9
|
+
|
|
10
|
+
export type FloatingProps = Partial<Record<Matrix<Variant, Size>, boolean>>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MotionValue } from 'framer-motion'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import layoutContext from '../context/layoutContext'
|
|
4
|
+
|
|
5
|
+
export type LayoutProviderProps = {
|
|
6
|
+
children: React.ReactNode
|
|
7
|
+
scroll: MotionValue<number>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function LayoutProvider(props: LayoutProviderProps) {
|
|
11
|
+
const { children, scroll } = props
|
|
12
|
+
return (
|
|
13
|
+
<layoutContext.Provider value={useMemo(() => ({ scroll }), [scroll])}>
|
|
14
|
+
{children}
|
|
15
|
+
</layoutContext.Provider>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { makeStyles, Theme, Typography, TypographyProps } from '@material-ui/core'
|
|
2
|
-
import clsx from 'clsx'
|
|
3
2
|
import React from 'react'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
3
|
+
import { UseStyles } from '../../Styles'
|
|
4
|
+
import { classesPicker } from '../../Styles/classesPicker'
|
|
5
|
+
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
6
|
+
import { SvgImageProps } from '../../SvgImage'
|
|
7
|
+
import SvgImageSimple from '../../SvgImage/SvgImageSimple'
|
|
8
8
|
|
|
9
9
|
const useStyles = makeStyles(
|
|
10
10
|
(theme: Theme) => ({
|
|
@@ -18,8 +18,7 @@ const useStyles = makeStyles(
|
|
|
18
18
|
flexFlow: 'column',
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
|
-
|
|
22
|
-
small: {
|
|
21
|
+
containerSizeSmall: {
|
|
23
22
|
flexFlow: 'unset',
|
|
24
23
|
'& svg': {
|
|
25
24
|
width: responsiveVal(24, 28),
|
|
@@ -27,10 +26,15 @@ const useStyles = makeStyles(
|
|
|
27
26
|
strokeWidth: 1.4,
|
|
28
27
|
},
|
|
29
28
|
},
|
|
29
|
+
containerGutterTop: {
|
|
30
|
+
marginTop: theme.spacings.xl,
|
|
31
|
+
},
|
|
32
|
+
containerGutterBottom: {
|
|
33
|
+
marginBottom: theme.spacings.lg,
|
|
34
|
+
},
|
|
35
|
+
typography: {},
|
|
30
36
|
}),
|
|
31
|
-
{
|
|
32
|
-
name: 'Title',
|
|
33
|
-
},
|
|
37
|
+
{ name: 'Title' },
|
|
34
38
|
)
|
|
35
39
|
|
|
36
40
|
export type TitleProps = {
|
|
@@ -38,17 +42,24 @@ export type TitleProps = {
|
|
|
38
42
|
icon?: SvgImageProps['src']
|
|
39
43
|
size?: 'small' | 'medium'
|
|
40
44
|
variant?: TypographyProps['variant']
|
|
45
|
+
gutterTop?: boolean
|
|
46
|
+
gutterBottom?: boolean
|
|
41
47
|
component?: React.ElementType
|
|
42
48
|
} & UseStyles<typeof useStyles>
|
|
43
49
|
|
|
44
|
-
const
|
|
50
|
+
export const LayoutTitle = React.forwardRef<HTMLDivElement, TitleProps>((props, ref) => {
|
|
45
51
|
const { children, icon, size = 'medium', component, variant } = props
|
|
46
52
|
const classes = useStyles(props)
|
|
47
53
|
const small = size === 'small'
|
|
48
54
|
|
|
55
|
+
const gutterTop = !!(props.gutterTop ?? size !== 'small')
|
|
56
|
+
const gutterBottom = !!(props.gutterBottom ?? size !== 'small')
|
|
57
|
+
|
|
58
|
+
const className = classesPicker(classes, { size, gutterBottom, gutterTop })
|
|
59
|
+
|
|
49
60
|
return (
|
|
50
|
-
<div className
|
|
51
|
-
{icon && <SvgImageSimple src={icon} size='xl' />}
|
|
61
|
+
<div {...className('container')}>
|
|
62
|
+
{icon && <SvgImageSimple src={icon} size={small ? 'large' : 'xl'} />}
|
|
52
63
|
<Typography
|
|
53
64
|
ref={ref}
|
|
54
65
|
variant={variant || (small ? 'h6' : 'h3')}
|
|
@@ -60,5 +71,3 @@ const Title = React.forwardRef<HTMLDivElement, TitleProps>((props, ref) => {
|
|
|
60
71
|
</div>
|
|
61
72
|
)
|
|
62
73
|
})
|
|
63
|
-
|
|
64
|
-
export default Title
|