@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.
Files changed (95) 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/CHANGELOG.md +30 -0
  20. package/ChipMenu/index.tsx +2 -2
  21. package/FlagAvatar/index.tsx +3 -3
  22. package/{AppShell/Footer/index.tsx → Footer/Footer.tsx} +3 -3
  23. package/{AppShell/Footer → Footer}/SocialIcon.tsx +3 -3
  24. package/Footer/index.ts +2 -0
  25. package/Form/InputCheckmark.tsx +3 -3
  26. package/Form/index.tsx +1 -1
  27. package/FramerScroller/components/SidebarGallery.tsx +24 -20
  28. package/FramerScroller/components/SidebarSlider.tsx +2 -6
  29. package/FullPageMessage/index.tsx +1 -1
  30. package/IconHeader/index.tsx +2 -15
  31. package/Layout/components/LayoutHeader.tsx +151 -0
  32. package/Layout/components/LayoutHeaderBack.tsx +50 -0
  33. package/Layout/components/LayoutHeaderClose.tsx +27 -0
  34. package/Layout/components/LayoutHeaderContent.tsx +173 -0
  35. package/Layout/components/LayoutHeadertypes.ts +10 -0
  36. package/Layout/components/LayoutProvider.tsx +17 -0
  37. package/{Title/index.tsx → Layout/components/LayoutTitle.tsx} +24 -15
  38. package/Layout/context/layoutContext.tsx +7 -0
  39. package/Layout/hooks/useScrollY.tsx +6 -0
  40. package/Layout/index.ts +5 -0
  41. package/Layout/types.ts +5 -0
  42. package/LayoutDefault/components/LayoutDefault.tsx +90 -0
  43. package/LayoutDefault/index.ts +1 -0
  44. package/LayoutOverlay/components/LayoutOverlay.tsx +25 -0
  45. package/LayoutOverlay/components/LayoutOverlayBase.tsx +354 -0
  46. package/LayoutOverlay/components/LayoutOverlayHeader.tsx +5 -0
  47. package/LayoutOverlay/hooks/useOverlayPosition.ts +70 -0
  48. package/LayoutOverlay/index.ts +2 -0
  49. package/Page/App.tsx +2 -0
  50. package/PageMeta/index.tsx +3 -0
  51. package/Pagination/index.tsx +0 -1
  52. package/Row/ButtonLinkList/index.tsx +1 -1
  53. package/Row/ColumnOneBoxed/index.tsx +1 -1
  54. package/Row/ColumnTwoWithTop/index.tsx +3 -3
  55. package/Row/ContentLinks/index.tsx +3 -3
  56. package/Row/HeroBanner/index.tsx +7 -11
  57. package/Row/IconBlocks/index.tsx +1 -1
  58. package/Row/ImageText/index.tsx +1 -1
  59. package/Row/ImageTextBoxed/index.tsx +1 -1
  60. package/Row/ParagraphWithSidebarSlide/index.tsx +1 -1
  61. package/Row/SpecialBanner/index.tsx +4 -3
  62. package/Row/index.tsx +1 -1
  63. package/Snackbar/MessageSnackbarImpl.tsx +1 -1
  64. package/StarRatingField/index.tsx +3 -4
  65. package/Stepper/Stepper.tsx +1 -1
  66. package/Styles/breakpointVal.tsx +2 -2
  67. package/Styles/classesPicker.ts +41 -0
  68. package/Styles/responsiveVal.tsx +1 -1
  69. package/SvgImage/SvgImageSimple.tsx +9 -8
  70. package/SvgImage/index.tsx +9 -11
  71. package/TextInputNumber/index.tsx +3 -4
  72. package/Theme/types.ts +14 -12
  73. package/ToggleButton/index.tsx +2 -4
  74. package/UspList/UspListItem.tsx +1 -1
  75. package/index.ts +9 -43
  76. package/package.json +8 -9
  77. package/AppShell/AppShellHeader/appShellHeaderContext.tsx +0 -11
  78. package/AppShell/AppShellHeader/index.tsx +0 -439
  79. package/AppShell/AppShellHeader/useAppShellHeaderContext.tsx +0 -6
  80. package/AppShell/AppShellProvider/index.tsx +0 -18
  81. package/AppShell/AppShellTitle/index.tsx +0 -45
  82. package/AppShell/ForwardButton.tsx +0 -53
  83. package/AppShell/FullPageShellBase.tsx +0 -82
  84. package/AppShell/MinimalPageShellBase.tsx +0 -22
  85. package/AppShell/PageShellHeader/index.tsx +0 -14
  86. package/AppShell/SheetShellBase/index.tsx +0 -114
  87. package/AppShell/SheetShellBase/useSheetStyles.ts +0 -18
  88. package/AppShell/SheetShellDragIndicator/index.tsx +0 -55
  89. package/AppShell/SheetShellHeader/index.tsx +0 -28
  90. package/AppShell/ShellBase.tsx +0 -45
  91. package/Debug/DebugSpacer.tsx +0 -51
  92. package/FramerNextPagesSlider/Slide.tsx +0 -71
  93. package/FramerNextPagesSlider/Slider.tsx +0 -39
  94. package/FramerNextPagesSlider/index.ts +0 -1
  95. 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 { 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
  },
@@ -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} hideScrollbar>
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) => ({
@@ -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 { 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 { responsiveVal } from '..'
5
- import { UseStyles } from '../Styles'
6
- import SvgImage, { SvgImageProps } from '../SvgImage'
7
- import SvgImageSimple from '../SvgImage/SvgImageSimple'
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
- typography: {},
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 Title = React.forwardRef<HTMLDivElement, TitleProps>((props, ref) => {
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={clsx(classes.container, small && classes.small)}>
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