@graphcommerce/next-ui 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/.babelrc +3 -0
  2. package/AnimatedRow/index.tsx +20 -0
  3. package/ApolloError/ApolloErrorAlert.tsx +59 -0
  4. package/ApolloError/ApolloErrorFullPage.tsx +25 -0
  5. package/AppShell/AppShellHeader/appShellHeaderContext.tsx +11 -0
  6. package/AppShell/AppShellHeader/index.tsx +433 -0
  7. package/AppShell/AppShellHeader/useAppShellHeaderContext.tsx +6 -0
  8. package/AppShell/AppShellProvider/index.tsx +18 -0
  9. package/AppShell/AppShellSticky/index.tsx +66 -0
  10. package/AppShell/AppShellTitle/index.tsx +45 -0
  11. package/AppShell/DesktopNavActions.tsx +28 -0
  12. package/AppShell/DesktopNavBar.tsx +110 -0
  13. package/AppShell/FixedFab.tsx +41 -0
  14. package/AppShell/ForwardButton.tsx +53 -0
  15. package/AppShell/FullPageShellBase.tsx +59 -0
  16. package/AppShell/Menu.tsx +7 -0
  17. package/AppShell/MenuFab.tsx +132 -0
  18. package/AppShell/MenuFabSecondaryItem.tsx +32 -0
  19. package/AppShell/MinimalPageShellBase.tsx +22 -0
  20. package/AppShell/PageShellHeader/index.tsx +14 -0
  21. package/AppShell/SheetShellBase/index.tsx +107 -0
  22. package/AppShell/SheetShellBase/useSheetStyles.ts +11 -0
  23. package/AppShell/SheetShellDragIndicator/index.tsx +48 -0
  24. package/AppShell/SheetShellHeader/index.tsx +28 -0
  25. package/AppShell/ShellBase.tsx +45 -0
  26. package/AppShell/useFabAnimation.tsx +19 -0
  27. package/AppShell/useFixedFabAnimation.tsx +18 -0
  28. package/AspectRatioContainer/index.tsx +27 -0
  29. package/Blog/BlogAuthor/index.tsx +60 -0
  30. package/Blog/BlogContent/index.tsx +24 -0
  31. package/Blog/BlogHeader/index.tsx +64 -0
  32. package/Blog/BlogList/index.tsx +27 -0
  33. package/Blog/BlogListItem/index.tsx +83 -0
  34. package/Blog/BlogTags/index.tsx +34 -0
  35. package/Blog/BlogTitle/index.tsx +29 -0
  36. package/Button/index.tsx +164 -0
  37. package/ButtonLink/index.tsx +45 -0
  38. package/CHANGELOG.md +1095 -0
  39. package/ChipMenu/index.tsx +130 -0
  40. package/ContainerWithHeader/index.tsx +49 -0
  41. package/Debug/DebugSpacer.tsx +51 -0
  42. package/FlagAvatar/index.tsx +28 -0
  43. package/Form/FormActions.tsx +15 -0
  44. package/Form/FormDivider.tsx +14 -0
  45. package/Form/FormHeader.tsx +27 -0
  46. package/Form/FormRow.tsx +14 -0
  47. package/Form/InputCheckmark.tsx +19 -0
  48. package/Form/index.tsx +62 -0
  49. package/FramerNextPagesSlider/Slide.tsx +71 -0
  50. package/FramerNextPagesSlider/Slider.tsx +39 -0
  51. package/FramerNextPagesSlider/index.ts +1 -0
  52. package/FramerNextPagesSlider/types.ts +3 -0
  53. package/FramerScroller/components/SidebarGallery.tsx +246 -0
  54. package/FramerScroller/components/SidebarSlider.tsx +103 -0
  55. package/FullPageMessage/index.tsx +68 -0
  56. package/Highlight/index.tsx +14 -0
  57. package/IconHeader/index.tsx +109 -0
  58. package/JsonLd/index.tsx +18 -0
  59. package/Page/App.tsx +15 -0
  60. package/Page/Document.tsx +23 -0
  61. package/Page/framerFeatures.ts +4 -0
  62. package/Page/types.ts +19 -0
  63. package/PageLoadIndicator/index.tsx +46 -0
  64. package/PageMeta/index.tsx +40 -0
  65. package/Pagination/index.tsx +123 -0
  66. package/RenderType/index.tsx +40 -0
  67. package/Row/ButtonLinkList/index.tsx +53 -0
  68. package/Row/ColumnOne/index.tsx +11 -0
  69. package/Row/ColumnOneBoxed/index.tsx +27 -0
  70. package/Row/ColumnOneCentered/index.tsx +22 -0
  71. package/Row/ColumnThree/index.tsx +66 -0
  72. package/Row/ColumnTwo/index.tsx +44 -0
  73. package/Row/ColumnTwoSpread/index.tsx +41 -0
  74. package/Row/ColumnTwoWithTop/index.tsx +53 -0
  75. package/Row/ContentLinks/index.tsx +48 -0
  76. package/Row/HeroBanner/index.tsx +102 -0
  77. package/Row/IconBlocks/IconBlock/index.tsx +56 -0
  78. package/Row/IconBlocks/index.tsx +57 -0
  79. package/Row/ParagraphWithSidebarSlide/index.tsx +114 -0
  80. package/Row/Quote/index.tsx +13 -0
  81. package/Row/RowImageText/index.tsx +67 -0
  82. package/Row/RowImageTextBoxed/index.tsx +75 -0
  83. package/Row/SpecialBanner/index.tsx +107 -0
  84. package/Row/index.tsx +13 -0
  85. package/SectionContainer/index.tsx +39 -0
  86. package/SectionHeader/index.tsx +69 -0
  87. package/Separator/index.tsx +33 -0
  88. package/Snackbar/ErrorSnackbar.tsx +9 -0
  89. package/Snackbar/MessageSnackbar.tsx +5 -0
  90. package/Snackbar/MessageSnackbarImpl.tsx +202 -0
  91. package/StarRatingField/index.tsx +58 -0
  92. package/Stepper/Stepper.tsx +45 -0
  93. package/StyledBadge/index.tsx +21 -0
  94. package/Styles/index.tsx +3 -0
  95. package/Styles/responsiveVal.tsx +20 -0
  96. package/SvgImage/SvgImageSimple.tsx +66 -0
  97. package/SvgImage/index.tsx +76 -0
  98. package/TextInputNumber/index.tsx +169 -0
  99. package/Theme/types.ts +63 -0
  100. package/TimeAgo/index.tsx +29 -0
  101. package/Title/index.tsx +71 -0
  102. package/ToggleButton/index.tsx +100 -0
  103. package/ToggleButtonGroup/index.tsx +112 -0
  104. package/UspList/UspListItem.tsx +46 -0
  105. package/UspList/index.tsx +32 -0
  106. package/icons/icon_addresses.svg +17 -0
  107. package/icons/icon_arrow_back.svg +1 -0
  108. package/icons/icon_arrow_forward.svg +1 -0
  109. package/icons/icon_box.svg +6 -0
  110. package/icons/icon_chat.svg +1 -0
  111. package/icons/icon_checkmark.svg +1 -0
  112. package/icons/icon_checkmark_green.svg +1 -0
  113. package/icons/icon_chevron_back.svg +8 -0
  114. package/icons/icon_chevron_down.svg +8 -0
  115. package/icons/icon_chevron_left.svg +8 -0
  116. package/icons/icon_chevron_right.svg +8 -0
  117. package/icons/icon_chevron_up.svg +8 -0
  118. package/icons/icon_close.svg +6 -0
  119. package/icons/icon_close_circle.svg +1 -0
  120. package/icons/icon_collapse_vertical.svg +11 -0
  121. package/icons/icon_customer_service.svg +6 -0
  122. package/icons/icon_email.svg +1 -0
  123. package/icons/icon_email_outline.svg +6 -0
  124. package/icons/icon_expand_vertical.svg +11 -0
  125. package/icons/icon_heart.svg +15 -0
  126. package/icons/icon_home.svg +6 -0
  127. package/icons/icon_id.svg +6 -0
  128. package/icons/icon_invoice_red.svg +7 -0
  129. package/icons/icon_location_red.svg +7 -0
  130. package/icons/icon_lock.svg +6 -0
  131. package/icons/icon_menu.svg +1 -0
  132. package/icons/icon_min.svg +1 -0
  133. package/icons/icon_newspaper.svg +6 -0
  134. package/icons/icon_party.svg +7 -0
  135. package/icons/icon_person.svg +6 -0
  136. package/icons/icon_person_alt.svg +6 -0
  137. package/icons/icon_person_alt_big.svg +15 -0
  138. package/icons/icon_phone.svg +1 -0
  139. package/icons/icon_plus.svg +1 -0
  140. package/icons/icon_sad_face.svg +11 -0
  141. package/icons/icon_sad_shoppingbag.svg +16 -0
  142. package/icons/icon_search.svg +1 -0
  143. package/icons/icon_shopping_bag.svg +7 -0
  144. package/icons/icon_shutdown.svg +6 -0
  145. package/icons/icon_star.svg +6 -0
  146. package/icons/icon_star_filled_muted.svg +6 -0
  147. package/icons/icon_star_yellow.svg +6 -0
  148. package/icons/index.tsx +42 -0
  149. package/index.ts +163 -0
  150. package/package.json +61 -0
  151. package/tsconfig.json +5 -0
  152. package/types.d.ts +13 -0
  153. package/useIntersectionObserver/index.tsx +31 -0
@@ -0,0 +1,28 @@
1
+ import useSheetContext from '@graphcommerce/framer-sheet/hooks/useSheetContext'
2
+ import { useElementScroll } from '@graphcommerce/framer-utils'
3
+ import React from 'react'
4
+ import AppShellHeader, { AppShellHeaderProps } from '../AppShellHeader'
5
+ import useSheetStyles from '../SheetShellBase/useSheetStyles'
6
+ import SheetShellDragIndicator from '../SheetShellDragIndicator'
7
+
8
+ type SheetShellHeaderProps = {
9
+ hideDragIndicator?: boolean
10
+ } & Omit<AppShellHeaderProps, 'scrollY'>
11
+
12
+ export default function SheetShellHeader(props: SheetShellHeaderProps) {
13
+ const { hideDragIndicator } = props
14
+ const { contentRef } = useSheetContext()
15
+ const { y } = useElementScroll(contentRef)
16
+ const sheetClasses = useSheetStyles()
17
+
18
+ return (
19
+ <AppShellHeader
20
+ {...props}
21
+ scrollY={y}
22
+ dragIndicator={
23
+ hideDragIndicator ? undefined : <SheetShellDragIndicator classes={sheetClasses} />
24
+ }
25
+ sheet
26
+ />
27
+ )
28
+ }
@@ -0,0 +1,45 @@
1
+ import { useTheme } from '@material-ui/core'
2
+ import Head from 'next/head'
3
+ import React from 'react'
4
+ import PageLoadIndicator from '../PageLoadIndicator'
5
+
6
+ export type PageLayoutBaseProps = { name: string; children?: React.ReactNode }
7
+
8
+ const ShellBase = (props: { name: string; children?: React.ReactNode }) => {
9
+ const { children, name } = props
10
+ const theme = useTheme()
11
+
12
+ return (
13
+ <>
14
+ <PageLoadIndicator />
15
+ <Head>
16
+ <meta name='theme-color' content={theme.palette.background.default} key='theme-color' />
17
+ <meta
18
+ name='viewport'
19
+ content='minimum-scale=1, initial-scale=1, width=device-width'
20
+ key='viewport'
21
+ />
22
+ <meta name='application-name' content={name} key='application-name' />
23
+ <meta
24
+ name='apple-mobile-web-app-capable'
25
+ content='yes'
26
+ key='apple-mobile-web-app-capable'
27
+ />
28
+ <meta
29
+ name='apple-mobile-web-app-status-bar-style'
30
+ content='default'
31
+ key='apple-mobile-web-app-status-bar-style'
32
+ />
33
+ <meta name='apple-mobile-web-app-title' content={name} key='apple-mobile-web-app-title' />
34
+ <meta name='format-detection' content='telephone=no' key='format-detection' />
35
+ <meta name='mobile-web-app-capable' content='yes' key='mobile-web-app-capable' />
36
+ <link rel='apple-touch-icon' href='/manifest/icon-512-512.png' key='apple-touch-icon' />
37
+ <link rel='manifest' href='/manifest.webmanifest' key='manifest' />
38
+ <link rel='shortcut icon' href='/manifest/favicon.ico' key='shortcut icon' />
39
+ </Head>
40
+ {children}
41
+ </>
42
+ )
43
+ }
44
+
45
+ export default ShellBase
@@ -0,0 +1,19 @@
1
+ import { useMediaQuery, useTheme } from '@material-ui/core'
2
+ import { useMotionTemplate, useTransform, useViewportScroll } from 'framer-motion'
3
+
4
+ export default function useFabAnimation() {
5
+ const theme = useTheme()
6
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
7
+ const { scrollY } = useViewportScroll()
8
+ const scrollTo = isMobile ? 0 : 130
9
+
10
+ const scale = useTransform(scrollY, [50, scrollTo], [0.4, 1])
11
+ const opacity = useTransform(scrollY, [50, scrollTo], [0, 1])
12
+ const opacity1 = useTransform(scrollY, [0, scrollTo], [0, 0.08])
13
+ const opacity2 = useTransform(scrollY, [0, scrollTo], [0, 0.1])
14
+ const filter = useMotionTemplate`
15
+ drop-shadow(0 1px 4px rgba(0,0,0,${opacity1}))
16
+ drop-shadow(0 4px 10px rgba(0,0,0,${opacity2}))`
17
+
18
+ return { filter, opacity, scale }
19
+ }
@@ -0,0 +1,18 @@
1
+ import { useMediaQuery, useTheme } from '@material-ui/core'
2
+ import { useMotionTemplate, useTransform, useViewportScroll } from 'framer-motion'
3
+
4
+ export default function useFixedFabAnimation() {
5
+ const theme = useTheme()
6
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
7
+ const { scrollY } = useViewportScroll()
8
+ const scrollTo = isMobile ? 0 : 60
9
+
10
+ const opacity = useTransform(scrollY, [50, scrollTo], [0, 1])
11
+ const opacity1 = useTransform(scrollY, [0, scrollTo], [0, 0.08])
12
+ const opacity2 = useTransform(scrollY, [0, scrollTo], [0, 0.1])
13
+ const filter = useMotionTemplate`
14
+ drop-shadow(0 1px 4px rgba(0,0,0,${opacity1}))
15
+ drop-shadow(0 4px 10px rgba(0,0,0,${opacity2}))`
16
+
17
+ return { filter, opacity }
18
+ }
@@ -0,0 +1,27 @@
1
+ import { makeStyles } from '@material-ui/styles'
2
+ import React from 'react'
3
+ import { UseStyles } from '../Styles'
4
+
5
+ const useStyles = makeStyles({
6
+ root: ({ width, height }: Props) => ({
7
+ position: `relative`,
8
+ paddingTop: `calc(100% / ${width} * ${height})`,
9
+ '& img, & video': {
10
+ position: 'absolute',
11
+ left: 0,
12
+ top: 0,
13
+ width: `100%`,
14
+ height: `auto`,
15
+ },
16
+ }),
17
+ })
18
+
19
+ export type Props = { width: number; height: number }
20
+ export type AspectRatioContainerProps = Props & UseStyles<typeof useStyles>
21
+
22
+ const AspectRatioContainer: React.FC<AspectRatioContainerProps> = ({ children, ...styleProps }) => {
23
+ const classes = useStyles(styleProps)
24
+ return <div className={classes.root}>{children}</div>
25
+ }
26
+
27
+ export default AspectRatioContainer
@@ -0,0 +1,60 @@
1
+ import { Avatar, Chip, makeStyles, Theme } from '@material-ui/core'
2
+ import React from 'react'
3
+ import Row from '../../Row'
4
+ import { UseStyles } from '../../Styles'
5
+
6
+ const useStyles = makeStyles(
7
+ (theme: Theme) => ({
8
+ wrapper: {
9
+ display: 'flex',
10
+ justifyContent: 'left',
11
+ maxWidth: 800,
12
+ margin: `0 auto`,
13
+ marginBottom: theme.spacings.md,
14
+ '& > div': {
15
+ height: 52,
16
+ borderRadius: 30,
17
+ '& > div': {
18
+ width: '44px !important',
19
+ height: '44px !important',
20
+ },
21
+ },
22
+ },
23
+ date: {
24
+ color: theme.palette.text.disabled,
25
+ },
26
+ author: {},
27
+ }),
28
+ { name: 'BlogAuthor' },
29
+ )
30
+
31
+ export type BlogAuthorProps = UseStyles<typeof useStyles> & {
32
+ author: string
33
+ date: string
34
+ locale: string
35
+ }
36
+
37
+ export default function BlogAuthor(props: BlogAuthorProps) {
38
+ const { author, date, locale } = props
39
+ const classes = useStyles()
40
+
41
+ const formatter = new Intl.DateTimeFormat(locale, {
42
+ month: 'long',
43
+ day: 'numeric',
44
+ })
45
+
46
+ return (
47
+ <div className={classes.wrapper}>
48
+ <Chip
49
+ variant='outlined'
50
+ avatar={<Avatar>{author.charAt(0).toUpperCase()}</Avatar>}
51
+ label={
52
+ <section>
53
+ <div className={classes.author}>{author}</div>
54
+ <div className={classes.date}>{formatter.format(new Date(date))}</div>
55
+ </section>
56
+ }
57
+ />
58
+ </div>
59
+ )
60
+ }
@@ -0,0 +1,24 @@
1
+ import { makeStyles, Theme } from '@material-ui/core'
2
+ import React from 'react'
3
+
4
+ const useStyles = makeStyles(
5
+ (theme: Theme) => ({
6
+ wrapper: {
7
+ maxWidth: 800,
8
+ margin: '0 auto',
9
+ marginBottom: theme.spacings.sm,
10
+ },
11
+ }),
12
+ { name: 'BlogContent' },
13
+ )
14
+
15
+ export type BlogContentProps = {
16
+ content: React.ReactElement
17
+ }
18
+
19
+ export default function BlogContent(props: BlogContentProps) {
20
+ const { content } = props
21
+ const classes = useStyles()
22
+
23
+ return <div className={classes.wrapper}>{content}</div>
24
+ }
@@ -0,0 +1,64 @@
1
+ import { makeStyles, Theme, Typography } from '@material-ui/core'
2
+ import React from 'react'
3
+ import Row from '../../Row'
4
+ import { UseStyles } from '../../Styles'
5
+
6
+ const useStyles = makeStyles(
7
+ (theme: Theme) => ({
8
+ header: {
9
+ maxWidth: 800,
10
+ margin: `0 auto`,
11
+ marginBottom: theme.spacings.md,
12
+ position: 'relative',
13
+ backgroundColor: 'rgba(0,0,0,0.08)',
14
+ overflow: 'hidden',
15
+ },
16
+ copy: {
17
+ color: '#fff',
18
+ display: 'grid',
19
+ justifyItems: 'end',
20
+ alignContent: 'end',
21
+ padding: `${theme.spacings.lg} ${theme.spacings.md}`,
22
+ minHeight: '30vh',
23
+ '& > *': {
24
+ zIndex: 0,
25
+ maxWidth: 'max-content',
26
+ },
27
+ [theme.breakpoints.up('lg')]: {
28
+ padding: `${theme.spacings.lg} ${theme.spacings.lg}`,
29
+ },
30
+ },
31
+ asset: {
32
+ position: 'absolute',
33
+ top: '0',
34
+ zIndex: 0,
35
+ width: '100%',
36
+ height: '100%',
37
+ '& img': {
38
+ width: '100%',
39
+ height: '100% !important',
40
+ objectFit: 'cover',
41
+ },
42
+ [theme.breakpoints.up('md')]: {
43
+ height: '100%',
44
+ },
45
+ },
46
+ }),
47
+ { name: 'BlogHeader' },
48
+ )
49
+
50
+ export type BlogHeaderProps = UseStyles<typeof useStyles> & {
51
+ asset?: React.ReactNode
52
+ }
53
+
54
+ export default function BlogHeader(props: BlogHeaderProps) {
55
+ const { asset } = props
56
+ const classes = useStyles()
57
+
58
+ return (
59
+ <div className={classes.header}>
60
+ <Typography variant='body1' className={classes.copy} />
61
+ {asset && <div className={classes.asset}>{asset}</div>}
62
+ </div>
63
+ )
64
+ }
@@ -0,0 +1,27 @@
1
+ import { makeStyles, Theme } from '@material-ui/core'
2
+ import React from 'react'
3
+ import Row from '../../Row'
4
+ import { UseStyles } from '../../Styles'
5
+ import responsiveVal from '../../Styles/responsiveVal'
6
+
7
+ const useStyles = makeStyles(
8
+ (theme: Theme) => ({
9
+ root: {
10
+ display: 'grid',
11
+ gap: theme.spacings.md,
12
+ gridTemplateColumns: `repeat(auto-fill, minmax(${responsiveVal(150, 285)}, 1fr))`,
13
+ },
14
+ }),
15
+ { name: 'BlogList' },
16
+ )
17
+
18
+ export type BlogListProps = UseStyles<typeof useStyles> & {
19
+ children: React.ReactElement
20
+ }
21
+
22
+ export default function BlogList(props: BlogListProps) {
23
+ const { children } = props
24
+ const classes = useStyles()
25
+
26
+ return <Row className={classes.root}>{children}</Row>
27
+ }
@@ -0,0 +1,83 @@
1
+ import { Link, makeStyles, Theme, Typography } from '@material-ui/core'
2
+ import PageLink from 'next/link'
3
+ import React from 'react'
4
+ import { UseStyles } from '../../Styles'
5
+ import responsiveVal from '../../Styles/responsiveVal'
6
+
7
+ const useStyles = makeStyles(
8
+ (theme: Theme) => ({
9
+ item: {
10
+ display: 'grid',
11
+ gridTemplateRows: `${responsiveVal(140, 220)} auto auto`,
12
+ alignContent: 'start',
13
+ color: theme.palette.text.primary,
14
+ gap: theme.spacings.sm,
15
+ marginBottom: theme.spacings.sm,
16
+ },
17
+ date: {
18
+ display: 'inline-block',
19
+ textDecoration: 'none',
20
+ color: 'rgb(0, 0, 0, 0.3)',
21
+ },
22
+ asset: {
23
+ display: 'grid',
24
+ overflow: 'hidden',
25
+ height: '100%',
26
+ width: '100%',
27
+ '& img': {
28
+ height: '100% !important',
29
+ objectFit: 'cover',
30
+ },
31
+ '& p': {
32
+ alignSelf: 'center',
33
+ justifySelf: 'center',
34
+ },
35
+ background: 'rgb(0, 0, 0, 0.08)',
36
+ },
37
+ title: {
38
+ ...theme.typography.h3,
39
+ },
40
+ }),
41
+ { name: 'BlogListItem' },
42
+ )
43
+
44
+ export type BlogListItemProps = UseStyles<typeof useStyles> & {
45
+ asset: React.ReactNode
46
+ url: string
47
+ date: string
48
+ locale: string
49
+ title: string
50
+ }
51
+
52
+ export default function BlogListItem(props: BlogListItemProps) {
53
+ const { asset, url, date, locale, title } = props
54
+ const classes = useStyles(props)
55
+
56
+ const formatter = new Intl.DateTimeFormat(locale, {
57
+ year: 'numeric',
58
+ month: 'long',
59
+ day: 'numeric',
60
+ })
61
+
62
+ return (
63
+ <div className={classes.item}>
64
+ <PageLink href={`/${url}`} passHref>
65
+ <Link color='inherit'>
66
+ <div className={classes.asset}>{asset}</div>
67
+ </Link>
68
+ </PageLink>
69
+
70
+ <time className={classes.date} dateTime={date}>
71
+ {formatter.format(new Date(date))}
72
+ </time>
73
+
74
+ <PageLink href={`/${url}`} passHref>
75
+ <Link href={`/${url}`} className={classes.title} color='inherit'>
76
+ <Typography component='h2' variant='h4' color='inherit'>
77
+ {title}
78
+ </Typography>
79
+ </Link>
80
+ </PageLink>
81
+ </div>
82
+ )
83
+ }
@@ -0,0 +1,34 @@
1
+ import { Chip, makeStyles, Theme } from '@material-ui/core'
2
+ import PageLink from 'next/link'
3
+ import React from 'react'
4
+
5
+ const useStyles = makeStyles(
6
+ (theme: Theme) => ({
7
+ wrapper: {
8
+ maxWidth: 800,
9
+ margin: `0 auto`,
10
+ marginBottom: theme.spacings.sm,
11
+ },
12
+ tag: {
13
+ marginRight: 8,
14
+ borderRadius: 4,
15
+ fontSize: 14,
16
+ },
17
+ }),
18
+ { name: 'BlogTitle' },
19
+ )
20
+
21
+ export default function BlogTitle(props) {
22
+ const { relatedPages } = props
23
+ const classes = useStyles()
24
+
25
+ return (
26
+ <div className={classes.wrapper}>
27
+ {relatedPages.map((page) => (
28
+ <PageLink key={page.url} href={`/${page.url}`} passHref>
29
+ <Chip label={page.title} className={classes.tag} />
30
+ </PageLink>
31
+ ))}
32
+ </div>
33
+ )
34
+ }
@@ -0,0 +1,29 @@
1
+ import { makeStyles, Theme, Typography } from '@material-ui/core'
2
+ import React from 'react'
3
+ import Row from '../../Row'
4
+ import { UseStyles } from '../../Styles'
5
+
6
+ const useStyles = makeStyles(
7
+ (theme: Theme) => ({
8
+ wrapper: {
9
+ maxWidth: 800,
10
+ margin: `0 auto`,
11
+ },
12
+ }),
13
+ { name: 'BlogTitle' },
14
+ )
15
+
16
+ export type BlogTitleProps = UseStyles<typeof useStyles> & {
17
+ title: string
18
+ }
19
+
20
+ export default function BlogTitle(props: BlogTitleProps) {
21
+ const { title } = props
22
+ const classes = useStyles()
23
+
24
+ return (
25
+ <div className={classes.wrapper}>
26
+ <Typography variant='h1'>{title}</Typography>
27
+ </div>
28
+ )
29
+ }
@@ -0,0 +1,164 @@
1
+ import { Button as MuiButton, ButtonClassKey as MuiButtonClassKey, Theme } from '@material-ui/core'
2
+ import { makeStyles } from '@material-ui/styles'
3
+ import clsx from 'clsx'
4
+ import React from 'react'
5
+
6
+ type BaseButtonProps = Omit<Parameters<typeof MuiButton>['0'], 'variant' | 'classes'> & {
7
+ variant?: 'text' | 'outlined' | 'contained' | 'pill' | 'pill-link'
8
+ }
9
+
10
+ type ButtonClassKey =
11
+ | 'pill'
12
+ | 'pillLink'
13
+ | 'pillPrimary'
14
+ | 'pillSecondary'
15
+ | 'pillSizeLarge'
16
+ | 'pillSizeSmall'
17
+ | 'pillNoElevation'
18
+ | 'textBold'
19
+ | 'withStartIcon'
20
+ | 'startIconText'
21
+
22
+ type ClassKeys = ButtonClassKey | MuiButtonClassKey
23
+ type Text = 'normal' | 'bold'
24
+
25
+ export type ButtonProps = BaseButtonProps & {
26
+ classes?: { [index in ClassKeys]?: string }
27
+ loading?: boolean
28
+ text?: Text
29
+ }
30
+
31
+ const useStyles = makeStyles<
32
+ Theme,
33
+ BaseButtonProps & { classes?: { [index in ButtonClassKey]?: string } },
34
+ ButtonClassKey
35
+ >(
36
+ (theme: Theme) => ({
37
+ root: {},
38
+ label: {},
39
+ disabled: {},
40
+ withStartIcon: {
41
+ [theme.breakpoints.down('sm')]: {
42
+ height: 40,
43
+ width: 40,
44
+ textAlign: 'center',
45
+ minWidth: 'unset',
46
+ borderRadius: 99,
47
+ '& > span > .MuiButton-startIcon': {
48
+ margin: 'unset',
49
+ },
50
+ },
51
+ },
52
+ pill: {
53
+ borderRadius: 40 / 2,
54
+ },
55
+ pillLink: {
56
+ [theme.breakpoints.up('md')]: {
57
+ background: theme.palette.secondary.main,
58
+ color: theme.palette.secondary.contrastText,
59
+ boxShadow: theme.shadows[2],
60
+ borderRadius: 25,
61
+ padding: '6px 16px',
62
+ fontWeight: theme.typography.fontWeightBold,
63
+ '&:hover': {
64
+ background: theme.palette.secondary.dark,
65
+ },
66
+ },
67
+ },
68
+ pillPrimary: {
69
+ //
70
+ },
71
+ pillSecondary: {
72
+ //
73
+ },
74
+ pillSizeLarge: {
75
+ borderRadius: 59 / 2,
76
+ },
77
+ pillSizeSmall: {
78
+ borderRadius: 33 / 2,
79
+ },
80
+ pillNoElevation: {
81
+ /* disableElevation does not stop adding box shadow on active... ?! */
82
+ '&:active': {
83
+ boxShadow: 'none',
84
+ },
85
+ },
86
+ textBold: {
87
+ fontWeight: theme.typography.fontWeightBold,
88
+ },
89
+ startIconText: {
90
+ display: 'none',
91
+ [theme.breakpoints.up('md')]: {
92
+ display: 'unset',
93
+ },
94
+ },
95
+ }),
96
+ { name: 'MuiPillButton' },
97
+ )
98
+
99
+ export default React.forwardRef<any, ButtonProps>((props, ref) => {
100
+ const { classes = {}, ...baseProps } = props
101
+ const { variant, color, size, className, children, loading, disabled, text, ...buttonProps } =
102
+ baseProps
103
+ const {
104
+ pill,
105
+ pillPrimary,
106
+ pillSecondary,
107
+ pillSizeLarge,
108
+ pillSizeSmall,
109
+ pillLink,
110
+ textBold,
111
+ ...buttonClasses
112
+ } = classes
113
+
114
+ const pillClasses = useStyles({
115
+ ...baseProps,
116
+ classes: {
117
+ pill,
118
+ pillPrimary,
119
+ pillSecondary,
120
+ pillSizeLarge,
121
+ pillSizeSmall,
122
+ pillLink,
123
+ textBold,
124
+ ...buttonClasses,
125
+ },
126
+ })
127
+
128
+ const variantMap = {
129
+ pill: 'contained',
130
+ 'pill-link': 'text',
131
+ }
132
+
133
+ const withIcon = typeof buttonProps.startIcon !== 'undefined'
134
+ const content = <>{loading ? <>Loading</> : children}</>
135
+
136
+ return (
137
+ <MuiButton
138
+ {...buttonProps}
139
+ classes={buttonClasses}
140
+ color={color}
141
+ variant={variantMap[variant ?? ''] ?? variant}
142
+ size={size}
143
+ ref={ref}
144
+ disabled={loading || disabled}
145
+ className={clsx(
146
+ {
147
+ [pillClasses.pill]: variant === 'pill',
148
+ [pillClasses.pillPrimary]: variant === 'pill' && color === 'primary',
149
+ [pillClasses.pillSecondary]: variant === 'pill' && color === 'secondary',
150
+ [pillClasses.pillSizeLarge]: variant === 'pill' && size === 'large',
151
+ [pillClasses.pillSizeSmall]: variant === 'pill' && size === 'small',
152
+ [pillClasses.pillNoElevation]: buttonProps.disableElevation,
153
+ [pillClasses.pillLink]: variant === 'pill-link',
154
+ [pillClasses.textBold]: text === 'bold',
155
+ [pillClasses.withStartIcon]: withIcon,
156
+ },
157
+ className,
158
+ )}
159
+ >
160
+ {withIcon && <span className={pillClasses.startIconText}>{content}</span>}
161
+ {!withIcon && content}
162
+ </MuiButton>
163
+ )
164
+ })
@@ -0,0 +1,45 @@
1
+ import { makeStyles, Theme } from '@material-ui/core'
2
+ import PageLink from 'next/link'
3
+ import React from 'react'
4
+ import Button, { ButtonProps } from '../Button'
5
+ import { UseStyles } from '../Styles'
6
+ import SvgImage from '../SvgImage'
7
+ import { iconChevronRight } from '../icons'
8
+
9
+ const useStyles = makeStyles((theme: Theme) => ({
10
+ buttonLink: {
11
+ color: theme.palette.text.primary,
12
+ textDecoration: 'none',
13
+ padding: `${theme.spacings.xs} 0`,
14
+ borderBottom: `1px solid ${theme.palette.grey[300]}`,
15
+ borderRadius: 0,
16
+ ...theme.typography.body1,
17
+ '& > *': {
18
+ display: 'grid',
19
+ gridAutoFlow: 'column',
20
+ justifyContent: 'space-between',
21
+ gap: `${theme.spacings.xs}`,
22
+ },
23
+ },
24
+ }))
25
+
26
+ export type ButtonLinkProps = {
27
+ title: string
28
+ url: string
29
+ endIcon?: React.ReactNode
30
+ } & ButtonProps &
31
+ UseStyles<typeof useStyles>
32
+
33
+ export default function ButtonLink(props: ButtonLinkProps) {
34
+ const { title, url, endIcon, ...buttonProps } = props
35
+ const classes = useStyles(props)
36
+
37
+ return (
38
+ <PageLink href={url} passHref>
39
+ <Button {...buttonProps} className={classes.buttonLink}>
40
+ <span>{title}</span>
41
+ {endIcon ?? <SvgImage src={iconChevronRight} alt='chevron right' />}
42
+ </Button>
43
+ </PageLink>
44
+ )
45
+ }