@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.
- package/.babelrc +3 -0
- package/AnimatedRow/index.tsx +20 -0
- package/ApolloError/ApolloErrorAlert.tsx +59 -0
- package/ApolloError/ApolloErrorFullPage.tsx +25 -0
- package/AppShell/AppShellHeader/appShellHeaderContext.tsx +11 -0
- package/AppShell/AppShellHeader/index.tsx +433 -0
- package/AppShell/AppShellHeader/useAppShellHeaderContext.tsx +6 -0
- package/AppShell/AppShellProvider/index.tsx +18 -0
- package/AppShell/AppShellSticky/index.tsx +66 -0
- package/AppShell/AppShellTitle/index.tsx +45 -0
- package/AppShell/DesktopNavActions.tsx +28 -0
- package/AppShell/DesktopNavBar.tsx +110 -0
- package/AppShell/FixedFab.tsx +41 -0
- package/AppShell/ForwardButton.tsx +53 -0
- package/AppShell/FullPageShellBase.tsx +59 -0
- package/AppShell/Menu.tsx +7 -0
- package/AppShell/MenuFab.tsx +132 -0
- package/AppShell/MenuFabSecondaryItem.tsx +32 -0
- package/AppShell/MinimalPageShellBase.tsx +22 -0
- package/AppShell/PageShellHeader/index.tsx +14 -0
- package/AppShell/SheetShellBase/index.tsx +107 -0
- package/AppShell/SheetShellBase/useSheetStyles.ts +11 -0
- package/AppShell/SheetShellDragIndicator/index.tsx +48 -0
- package/AppShell/SheetShellHeader/index.tsx +28 -0
- package/AppShell/ShellBase.tsx +45 -0
- package/AppShell/useFabAnimation.tsx +19 -0
- package/AppShell/useFixedFabAnimation.tsx +18 -0
- package/AspectRatioContainer/index.tsx +27 -0
- package/Blog/BlogAuthor/index.tsx +60 -0
- package/Blog/BlogContent/index.tsx +24 -0
- package/Blog/BlogHeader/index.tsx +64 -0
- package/Blog/BlogList/index.tsx +27 -0
- package/Blog/BlogListItem/index.tsx +83 -0
- package/Blog/BlogTags/index.tsx +34 -0
- package/Blog/BlogTitle/index.tsx +29 -0
- package/Button/index.tsx +164 -0
- package/ButtonLink/index.tsx +45 -0
- package/CHANGELOG.md +1095 -0
- package/ChipMenu/index.tsx +130 -0
- package/ContainerWithHeader/index.tsx +49 -0
- package/Debug/DebugSpacer.tsx +51 -0
- package/FlagAvatar/index.tsx +28 -0
- package/Form/FormActions.tsx +15 -0
- package/Form/FormDivider.tsx +14 -0
- package/Form/FormHeader.tsx +27 -0
- package/Form/FormRow.tsx +14 -0
- package/Form/InputCheckmark.tsx +19 -0
- package/Form/index.tsx +62 -0
- package/FramerNextPagesSlider/Slide.tsx +71 -0
- package/FramerNextPagesSlider/Slider.tsx +39 -0
- package/FramerNextPagesSlider/index.ts +1 -0
- package/FramerNextPagesSlider/types.ts +3 -0
- package/FramerScroller/components/SidebarGallery.tsx +246 -0
- package/FramerScroller/components/SidebarSlider.tsx +103 -0
- package/FullPageMessage/index.tsx +68 -0
- package/Highlight/index.tsx +14 -0
- package/IconHeader/index.tsx +109 -0
- package/JsonLd/index.tsx +18 -0
- package/Page/App.tsx +15 -0
- package/Page/Document.tsx +23 -0
- package/Page/framerFeatures.ts +4 -0
- package/Page/types.ts +19 -0
- package/PageLoadIndicator/index.tsx +46 -0
- package/PageMeta/index.tsx +40 -0
- package/Pagination/index.tsx +123 -0
- package/RenderType/index.tsx +40 -0
- package/Row/ButtonLinkList/index.tsx +53 -0
- package/Row/ColumnOne/index.tsx +11 -0
- package/Row/ColumnOneBoxed/index.tsx +27 -0
- package/Row/ColumnOneCentered/index.tsx +22 -0
- package/Row/ColumnThree/index.tsx +66 -0
- package/Row/ColumnTwo/index.tsx +44 -0
- package/Row/ColumnTwoSpread/index.tsx +41 -0
- package/Row/ColumnTwoWithTop/index.tsx +53 -0
- package/Row/ContentLinks/index.tsx +48 -0
- package/Row/HeroBanner/index.tsx +102 -0
- package/Row/IconBlocks/IconBlock/index.tsx +56 -0
- package/Row/IconBlocks/index.tsx +57 -0
- package/Row/ParagraphWithSidebarSlide/index.tsx +114 -0
- package/Row/Quote/index.tsx +13 -0
- package/Row/RowImageText/index.tsx +67 -0
- package/Row/RowImageTextBoxed/index.tsx +75 -0
- package/Row/SpecialBanner/index.tsx +107 -0
- package/Row/index.tsx +13 -0
- package/SectionContainer/index.tsx +39 -0
- package/SectionHeader/index.tsx +69 -0
- package/Separator/index.tsx +33 -0
- package/Snackbar/ErrorSnackbar.tsx +9 -0
- package/Snackbar/MessageSnackbar.tsx +5 -0
- package/Snackbar/MessageSnackbarImpl.tsx +202 -0
- package/StarRatingField/index.tsx +58 -0
- package/Stepper/Stepper.tsx +45 -0
- package/StyledBadge/index.tsx +21 -0
- package/Styles/index.tsx +3 -0
- package/Styles/responsiveVal.tsx +20 -0
- package/SvgImage/SvgImageSimple.tsx +66 -0
- package/SvgImage/index.tsx +76 -0
- package/TextInputNumber/index.tsx +169 -0
- package/Theme/types.ts +63 -0
- package/TimeAgo/index.tsx +29 -0
- package/Title/index.tsx +71 -0
- package/ToggleButton/index.tsx +100 -0
- package/ToggleButtonGroup/index.tsx +112 -0
- package/UspList/UspListItem.tsx +46 -0
- package/UspList/index.tsx +32 -0
- package/icons/icon_addresses.svg +17 -0
- package/icons/icon_arrow_back.svg +1 -0
- package/icons/icon_arrow_forward.svg +1 -0
- package/icons/icon_box.svg +6 -0
- package/icons/icon_chat.svg +1 -0
- package/icons/icon_checkmark.svg +1 -0
- package/icons/icon_checkmark_green.svg +1 -0
- package/icons/icon_chevron_back.svg +8 -0
- package/icons/icon_chevron_down.svg +8 -0
- package/icons/icon_chevron_left.svg +8 -0
- package/icons/icon_chevron_right.svg +8 -0
- package/icons/icon_chevron_up.svg +8 -0
- package/icons/icon_close.svg +6 -0
- package/icons/icon_close_circle.svg +1 -0
- package/icons/icon_collapse_vertical.svg +11 -0
- package/icons/icon_customer_service.svg +6 -0
- package/icons/icon_email.svg +1 -0
- package/icons/icon_email_outline.svg +6 -0
- package/icons/icon_expand_vertical.svg +11 -0
- package/icons/icon_heart.svg +15 -0
- package/icons/icon_home.svg +6 -0
- package/icons/icon_id.svg +6 -0
- package/icons/icon_invoice_red.svg +7 -0
- package/icons/icon_location_red.svg +7 -0
- package/icons/icon_lock.svg +6 -0
- package/icons/icon_menu.svg +1 -0
- package/icons/icon_min.svg +1 -0
- package/icons/icon_newspaper.svg +6 -0
- package/icons/icon_party.svg +7 -0
- package/icons/icon_person.svg +6 -0
- package/icons/icon_person_alt.svg +6 -0
- package/icons/icon_person_alt_big.svg +15 -0
- package/icons/icon_phone.svg +1 -0
- package/icons/icon_plus.svg +1 -0
- package/icons/icon_sad_face.svg +11 -0
- package/icons/icon_sad_shoppingbag.svg +16 -0
- package/icons/icon_search.svg +1 -0
- package/icons/icon_shopping_bag.svg +7 -0
- package/icons/icon_shutdown.svg +6 -0
- package/icons/icon_star.svg +6 -0
- package/icons/icon_star_filled_muted.svg +6 -0
- package/icons/icon_star_yellow.svg +6 -0
- package/icons/index.tsx +42 -0
- package/index.ts +163 -0
- package/package.json +61 -0
- package/tsconfig.json +5 -0
- package/types.d.ts +13 -0
- 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
|
+
}
|
package/Button/index.tsx
ADDED
|
@@ -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
|
+
}
|