@graphcommerce/next-ui 3.19.0 → 3.20.2

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 (96) hide show
  1. package/AppShell/AppShellSticky/index.tsx +3 -19
  2. package/AppShell/DesktopNavActions.tsx +3 -4
  3. package/AppShell/DesktopNavBar.tsx +2 -3
  4. package/AppShell/FixedFab.tsx +1 -6
  5. package/AppShell/GlobalHead.tsx +36 -0
  6. package/AppShell/Logo.tsx +11 -20
  7. package/AppShell/MenuFab.tsx +5 -3
  8. package/AppShell/MenuFabSecondaryItem.tsx +3 -3
  9. package/AppShell/PlaceholderFab/index.tsx +8 -27
  10. package/AppShell/index.tsx +19 -0
  11. package/AppShell/useFabAnimation.tsx +3 -2
  12. package/AppShell/useFixedFabAnimation.tsx +3 -2
  13. package/Blog/BlogAuthor/index.tsx +1 -1
  14. package/Blog/BlogHeader/index.tsx +2 -3
  15. package/Blog/BlogList/index.tsx +1 -1
  16. package/Blog/BlogListItem/index.tsx +1 -1
  17. package/Blog/BlogTitle/index.tsx +5 -5
  18. package/Button/index.tsx +26 -21
  19. package/ButtonLink/index.tsx +1 -1
  20. package/CHANGELOG.md +56 -0
  21. package/ChipMenu/index.tsx +2 -2
  22. package/FlagAvatar/index.tsx +3 -3
  23. package/{AppShell/Footer/index.tsx → Footer/Footer.tsx} +3 -3
  24. package/{AppShell/Footer → Footer}/SocialIcon.tsx +3 -3
  25. package/Footer/index.ts +2 -0
  26. package/Form/InputCheckmark.tsx +3 -3
  27. package/Form/index.tsx +1 -1
  28. package/FramerScroller/components/SidebarGallery.tsx +24 -20
  29. package/FramerScroller/components/SidebarSlider.tsx +1 -3
  30. package/FullPageMessage/index.tsx +1 -1
  31. package/IconHeader/index.tsx +2 -15
  32. package/Layout/components/LayoutHeader.tsx +151 -0
  33. package/Layout/components/LayoutHeaderBack.tsx +58 -0
  34. package/Layout/components/LayoutHeaderClose.tsx +27 -0
  35. package/Layout/components/LayoutHeaderContent.tsx +182 -0
  36. package/Layout/components/LayoutHeadertypes.ts +10 -0
  37. package/Layout/components/LayoutProvider.tsx +17 -0
  38. package/{Title/index.tsx → Layout/components/LayoutTitle.tsx} +24 -15
  39. package/Layout/context/layoutContext.tsx +7 -0
  40. package/Layout/hooks/useScrollY.tsx +6 -0
  41. package/Layout/index.ts +5 -0
  42. package/Layout/types.ts +5 -0
  43. package/LayoutDefault/components/LayoutDefault.tsx +90 -0
  44. package/LayoutDefault/index.ts +1 -0
  45. package/LayoutOverlay/components/LayoutOverlay.tsx +25 -0
  46. package/LayoutOverlay/components/LayoutOverlayBase.tsx +354 -0
  47. package/LayoutOverlay/components/LayoutOverlayHeader.tsx +5 -0
  48. package/LayoutOverlay/hooks/useOverlayPosition.ts +70 -0
  49. package/LayoutOverlay/index.ts +2 -0
  50. package/Page/App.tsx +2 -0
  51. package/PageMeta/index.tsx +3 -0
  52. package/Pagination/index.tsx +0 -1
  53. package/Row/ButtonLinkList/index.tsx +1 -1
  54. package/Row/ColumnOneBoxed/index.tsx +1 -1
  55. package/Row/ColumnTwoWithTop/index.tsx +3 -3
  56. package/Row/ContentLinks/index.tsx +3 -3
  57. package/Row/HeroBanner/index.tsx +7 -11
  58. package/Row/IconBlocks/index.tsx +1 -1
  59. package/Row/ImageText/index.tsx +1 -1
  60. package/Row/ImageTextBoxed/index.tsx +1 -1
  61. package/Row/ParagraphWithSidebarSlide/index.tsx +1 -1
  62. package/Row/SpecialBanner/index.tsx +4 -3
  63. package/Row/index.tsx +1 -1
  64. package/Snackbar/MessageSnackbarImpl.tsx +1 -1
  65. package/StarRatingField/index.tsx +3 -4
  66. package/Stepper/Stepper.tsx +1 -1
  67. package/Styles/breakpointVal.tsx +2 -2
  68. package/Styles/classesPicker.ts +41 -0
  69. package/Styles/responsiveVal.tsx +1 -1
  70. package/SvgImage/SvgImageSimple.tsx +9 -8
  71. package/SvgImage/index.tsx +9 -11
  72. package/TextInputNumber/index.tsx +3 -4
  73. package/Theme/types.ts +14 -12
  74. package/ToggleButton/index.tsx +2 -4
  75. package/UspList/UspListItem.tsx +1 -1
  76. package/index.ts +9 -43
  77. package/package.json +8 -9
  78. package/AppShell/AppShellHeader/appShellHeaderContext.tsx +0 -11
  79. package/AppShell/AppShellHeader/index.tsx +0 -439
  80. package/AppShell/AppShellHeader/useAppShellHeaderContext.tsx +0 -6
  81. package/AppShell/AppShellProvider/index.tsx +0 -18
  82. package/AppShell/AppShellTitle/index.tsx +0 -45
  83. package/AppShell/ForwardButton.tsx +0 -53
  84. package/AppShell/FullPageShellBase.tsx +0 -82
  85. package/AppShell/MinimalPageShellBase.tsx +0 -22
  86. package/AppShell/PageShellHeader/index.tsx +0 -14
  87. package/AppShell/SheetShellBase/index.tsx +0 -114
  88. package/AppShell/SheetShellBase/useSheetStyles.ts +0 -18
  89. package/AppShell/SheetShellDragIndicator/index.tsx +0 -55
  90. package/AppShell/SheetShellHeader/index.tsx +0 -28
  91. package/AppShell/ShellBase.tsx +0 -45
  92. package/Debug/DebugSpacer.tsx +0 -51
  93. package/FramerNextPagesSlider/Slide.tsx +0 -71
  94. package/FramerNextPagesSlider/Slider.tsx +0 -39
  95. package/FramerNextPagesSlider/index.ts +0 -1
  96. package/FramerNextPagesSlider/types.ts +0 -3
@@ -1,4 +1,4 @@
1
- import { makeStyles, Theme } from '@material-ui/core'
1
+ import { makeStyles } from '@material-ui/core'
2
2
  import clsx from 'clsx'
3
3
  import React, { PropsWithChildren } from 'react'
4
4
  import SvgImageSimple from '../SvgImage/SvgImageSimple'
@@ -6,14 +6,14 @@ import { iconCheckmark } from '../icons'
6
6
 
7
7
  export type InputCheckmarkProps = PropsWithChildren<{ show?: boolean; select?: boolean }>
8
8
  const useStyles = makeStyles(
9
- (theme: Theme) => ({
9
+ {
10
10
  iconCheckmark: {
11
11
  stroke: '#01D26A',
12
12
  },
13
13
  select: {
14
14
  marginRight: 15,
15
15
  },
16
- }),
16
+ },
17
17
  { name: 'InputCheckmark' },
18
18
  )
19
19
 
package/Form/index.tsx CHANGED
@@ -1,8 +1,8 @@
1
1
  import { darken, lighten, makeStyles, Theme } from '@material-ui/core'
2
2
  import clsx from 'clsx'
3
3
  import React from 'react'
4
- import { responsiveVal } from '..'
5
4
  import { UseStyles } from '../Styles'
5
+ import { responsiveVal } from '../Styles/responsiveVal'
6
6
 
7
7
  const useStyles = makeStyles(
8
8
  (theme: Theme) => ({
@@ -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 { Row } from '../..'
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.page.headerInnerHeight.sm} * -1)`,
46
+ marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
46
47
  [theme.breakpoints.up('md')]: {
47
- marginTop: `calc(${theme.page.headerInnerHeight.md} * -1 - ${theme.spacings.sm})`,
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.page.headerInnerHeight.sm} - ${theme.spacings.sm} * 2`
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 { sidebar, images, aspectRatio = [1, 1], routeHash = 'gallery' } = props
176
- const router = useRouter()
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: props.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 clsxZoom = (key: string) => clsx(classes?.[key], zoomed && classes?.[`${key}Zoomed`])
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={clsxZoom('root')}>
235
+ <m.div layout {...className('root')}>
232
236
  <m.div
233
237
  layout
234
- className={clsxZoom('scrollerContainer')}
238
+ {...className('scrollerContainer')}
235
239
  onLayoutAnimationComplete={() => {
236
240
  if (!zoomed) document.body.style.overflow = ''
237
241
  }}
238
242
  >
239
243
  <Scroller
240
- className={clsxZoom('scroller')}
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={clsxZoom('sidebarWrapper')}>
303
- <m.div layout className={clsxZoom('sidebar')}>
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
  },
@@ -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) => ({
@@ -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 { SvgImageSimple } from '..'
5
- import responsiveVal from '../Styles/responsiveVal'
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 { makeStyles, Theme } from '@material-ui/core'
2
+ import React from 'react'
3
+ import { classesPicker } from '../../Styles/classesPicker'
4
+ import LayoutHeaderBack, { useShowBack } from './LayoutHeaderBack'
5
+ import LayoutHeaderClose, { useShowClose } from './LayoutHeaderClose'
6
+ import LayoutHeaderContent, { ContentProps } from './LayoutHeaderContent'
7
+ import { FloatingProps } from './LayoutHeadertypes'
8
+
9
+ export type LayoutHeaderProps = FloatingProps &
10
+ Omit<ContentProps, 'left' | 'right'> & {
11
+ /**
12
+ * Button to display on the left side of the title
13
+ *
14
+ * - Assumes it can float on desktop
15
+ * - Assumes it can not float on mobile
16
+ */
17
+ primary?: React.ReactNode
18
+ /**
19
+ * Button to display on the right side of the title
20
+ *
21
+ * - Assumes it can float on desktop
22
+ * - Assumes it can not float on mobile
23
+ */
24
+ secondary?: React.ReactNode
25
+
26
+ additional?: React.ReactNode
27
+
28
+ noAlign?: boolean
29
+ }
30
+
31
+ const useStyles = makeStyles(
32
+ (theme: Theme) => ({
33
+ sticky: {
34
+ zIndex: theme.zIndex.appBar,
35
+ position: 'sticky',
36
+ pointerEvents: 'none',
37
+
38
+ [theme.breakpoints.up('md')]: {
39
+ top: 0,
40
+ height: theme.appShell.appBarHeightMd,
41
+ marginTop: `calc((${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * -0.5)`,
42
+ marginBottom: `calc(${theme.appShell.appBarHeightMd} * -1 - calc((${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * -0.5))`,
43
+ },
44
+ },
45
+ stickyNoChildren: {
46
+ zIndex: theme.zIndex.appBar - 2,
47
+ },
48
+ stickyVisibleSm: {
49
+ [theme.breakpoints.down('sm')]: {
50
+ top: 0,
51
+ marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
52
+ height: theme.appShell.headerHeightSm,
53
+ },
54
+ },
55
+ stickyFloatingSm: {
56
+ [theme.breakpoints.down('sm')]: {
57
+ top: 0,
58
+ marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
59
+ height: theme.appShell.headerHeightSm,
60
+ },
61
+ },
62
+ stickyFloatingMd: {
63
+ [theme.breakpoints.up('md')]: {
64
+ top: `calc(${theme.appShell.headerHeightMd} + calc((${theme.appShell.appBarHeightMd} - ${theme.appShell.appBarInnerHeightMd}) * -0.5))`,
65
+ },
66
+ },
67
+ stickyNoAlign: {
68
+ [theme.breakpoints.down('sm')]: {
69
+ position: 'sticky',
70
+ left: 0,
71
+ right: 0,
72
+ top: 0,
73
+ marginTop: 0,
74
+ height: theme.appShell.headerHeightSm,
75
+ marginBottom: `calc(${theme.appShell.headerHeightSm} * -1)`,
76
+ },
77
+ [theme.breakpoints.up('md')]: {
78
+ position: 'sticky',
79
+ left: 0,
80
+ right: 0,
81
+ top: 0,
82
+ marginTop: 0,
83
+ height: theme.appShell.appBarHeightMd,
84
+ marginBottom: `calc(${theme.appShell.appBarHeightMd} * -1)`,
85
+ },
86
+ },
87
+ stickyDivider: {
88
+ [theme.breakpoints.down('sm')]: {
89
+ marginBottom: 0,
90
+ },
91
+ [theme.breakpoints.up('md')]: {
92
+ marginBottom: 0,
93
+ },
94
+ },
95
+ }),
96
+ { name: 'LayoutHeader' },
97
+ )
98
+
99
+ export function LayoutHeader(props: LayoutHeaderProps) {
100
+ const { children, additional, divider, primary, secondary, noAlign, switchPoint } = props
101
+ const classes = useStyles(props)
102
+ const showBack = useShowBack()
103
+ const showClose = useShowClose()
104
+
105
+ const floatFallback = !children
106
+ let { floatingSm = false, floatingMd = floatFallback } = props
107
+
108
+ if (divider) floatingMd = false
109
+
110
+ // When the primary or secondary is set, the header can't float on mobile even if the prop is passed.
111
+ if (divider || primary || secondary) floatingSm = false
112
+
113
+ const close = showClose && <LayoutHeaderClose />
114
+ const back = showBack && <LayoutHeaderBack variant={floatingSm ? 'pill' : 'pill-link'} />
115
+
116
+ let left = secondary
117
+ let right = primary
118
+
119
+ if (back) left = back
120
+
121
+ if (!left) left = close
122
+ else if (!right) right = close
123
+
124
+ if (!left && !right && !children) return null
125
+
126
+ const className = classesPicker(classes, {
127
+ floatingSm,
128
+ floatingMd,
129
+ visibleSm: !floatingSm,
130
+ visibleMd: !floatingMd,
131
+ noChildren: !children,
132
+ noAlign,
133
+ divider: !!divider,
134
+ })
135
+
136
+ return (
137
+ <div {...className('sticky')}>
138
+ <LayoutHeaderContent
139
+ left={left}
140
+ right={right}
141
+ divider={divider}
142
+ floatingMd={floatingMd}
143
+ floatingSm={floatingSm}
144
+ switchPoint={switchPoint}
145
+ >
146
+ {children}
147
+ {additional}
148
+ </LayoutHeaderContent>
149
+ </div>
150
+ )
151
+ }
@@ -0,0 +1,58 @@
1
+ import { usePageRouter, useUp, usePrevUp, usePageContext } from '@graphcommerce/framer-next-pages'
2
+ import { t } 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
+ const label = up?.href === router.asPath ? up.title : t`Back`
35
+ return (
36
+ <Button
37
+ onClick={() => router.back()}
38
+ variant='pill-link'
39
+ startIcon={backIcon}
40
+ aria-label={label}
41
+ {...props}
42
+ >
43
+ {label}
44
+ </Button>
45
+ )
46
+ }
47
+
48
+ if (up?.href && up.href !== router.asPath)
49
+ return (
50
+ <PageLink href={up.href} passHref>
51
+ <Button variant='pill-link' startIcon={backIcon} aria-label={up.title} {...props}>
52
+ {up.title}
53
+ </Button>
54
+ </PageLink>
55
+ )
56
+
57
+ return null
58
+ }
@@ -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,182 @@
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
+ switchPoint?: number
145
+ }
146
+
147
+ export default function LayoutHeaderContent(props: ContentProps) {
148
+ const ref = useRef<HTMLDivElement>(null)
149
+ const scroll = useScrollY()
150
+
151
+ const {
152
+ left,
153
+ children,
154
+ right,
155
+ divider,
156
+ floatingMd = false,
157
+ floatingSm = false,
158
+ switchPoint = 50,
159
+ } = props
160
+ const classes = useStyles(props)
161
+
162
+ const scrolled = useMotionValueValue(scroll, (y) => y >= switchPoint)
163
+
164
+ const className = classesPicker<Classes>(classes, {
165
+ floatingSm,
166
+ floatingMd,
167
+ scrolled,
168
+ customDivider: !!divider,
169
+ })
170
+
171
+ return (
172
+ <>
173
+ <div {...className('bg')} />
174
+ <div {...className('content')} ref={ref}>
175
+ <div {...className('left')}>{left}</div>
176
+ <div {...className('center')}>{children}</div>
177
+ <div {...className('right')}>{right}</div>
178
+ <div {...className('divider')}>{divider ?? <Divider />}</div>
179
+ </div>
180
+ </>
181
+ )
182
+ }
@@ -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
+ }