fix: finish converting all primer/react to sx

Luke Karrys committed Oct 15, 2023 at 22:50 UTC 8582aa9343a7cd6b0da5d1592982237ee92d456e
25 files changed +396 -418
.eslintrc.js
+1
@@ -14,6 +14,7 @@ module.exports = {
14 ],
15 rules: {
16 'react/prop-types': 'off',
17 + 'primer-react/no-system-props': ['error', {includeUtilityComponents: true}],
18 },
19 overrides: [
20 {
src/components/clipboard-copy.js
+1 -1
@@ -33,7 +33,7 @@ function ClipboardCopy({value}) {
33 announce(`Copied to clipboard`)
34 }}
35 >
36 - {copied ? <Octicon icon={CheckIcon} color="green.5" /> : <Octicon icon={CopyIcon} color="gray.7" />}
36 + <Octicon icon={copied ? CheckIcon : CopyIcon} sx={{color: copied ? 'green.5' : 'gray.7'}} />
37 </CopyToClipboard>
38 )
39 }
src/components/container.js
+1 -7
@@ -1,12 +1,6 @@
1 import {Box} from '@primer/react'
2 import React from 'react'
3
4 -function Container({children}) {
5 - return (
6 - <Box width="100%" maxWidth={960} p={5} mx="auto">
7 - {children}
8 - </Box>
9 - )
10 -}
4 +const Container = ({children}) => <Box sx={{width: '100%', maxWidth: 960, p: 5, mx: 'auto'}}>{children}</Box>
5
6 export default Container
src/components/contributors.js
+3 -3
@@ -23,8 +23,8 @@ const pluralize = (word, count) => `${word}${count === 1 ? '' : 's'}`
23 function Contributors({logins, latestCommit}) {
24 return (
25 <div>
26 - <Box display="flex" alignItems="center">
27 - <Text mr={2}>
26 + <Box sx={{display: 'flex', alignItems: 'center'}}>
27 + <Text sx={{mr: 2}}>
28 {logins.length} {pluralize('contributor', logins.length)}
29 </Text>
30 {logins.map(login => (
@@ -37,7 +37,7 @@ function Contributors({logins, latestCommit}) {
37 ))}
38 </Box>
39 {latestCommit ? (
40 - <Text fontSize={1} color="gray.7" mt={1}>
40 + <Text sx={{fontSize: 1, color: 'gray.7', mt: 1}}>
41 Last edited by <Link href={`https://github.com/${latestCommit.login}`}>{latestCommit.login}</Link> on{' '}
42 <Link href={latestCommit.url}>{format(new Date(latestCommit.date))}</Link>
43 </Text>
src/components/dark-button.js deleted
-26
@@ -1,26 +0,0 @@
1 -import React from 'react'
2 -import {Button, themeGet} from '@primer/react'
3 -import styled from 'styled-components'
4 -
5 -const ButtonOutline = props => <Button variant="outline" {...props} />
6 -
7 -const DarkButton = styled(ButtonOutline)`
8 - color: ${themeGet('colors.gray.2')};
9 - background-color: transparent;
10 - border: 1px solid ${themeGet('colors.gray.7')};
11 - box-shadow: none;
12 -
13 - &:hover {
14 - background-color: #cb0000;
15 - }
16 -
17 - &:focus {
18 - box-shadow: 0 0 0 3px rgba(214, 102, 102, 0.3);
19 - }
20 -
21 - &:active {
22 - background-color: #ba0000;
23 - }
24 -`
25 -
26 -export default DarkButton
src/components/drawer.js
+19 -15
@@ -17,36 +17,40 @@ const Drawer = ({isOpen, onDismiss, children}) => (
17 role="button"
18 tabIndex="0"
19 >
20 - <FocusOn returnFocus={true} onEscapeKey={() => onDismiss()}>
20 + <FocusOn returnFocus={true} onEscapeKey={onDismiss}>
21 <Box
22 - position="fixed"
22 + sx={{
23 + position: 'fixed',
24 + top: 0,
25 + right: 0,
26 + bottom: 0,
27 + left: 0,
28 + bg: 'rgba(0, 0, 0, 0.5)',
29 + }}
30 key="overlay"
31 as={motion.div}
32 initial={{opacity: 0}}
33 animate={{opacity: 1}}
34 exit={{opacity: 0}}
35 transition={{type: 'tween'}}
29 - top={0}
30 - right={0}
31 - bottom={0}
32 - left={0}
33 - bg="rgba(0, 0, 0, 0.5)"
34 - onClick={() => onDismiss()}
36 + onClick={onDismiss}
37 />
38 <Box
37 - position="fixed"
39 + sx={{
40 + position: 'fixed',
41 + top: 0,
42 + right: 0,
43 + bottom: 0,
44 + bg: 'gray.0',
45 + width: 300,
46 + zIndex: 1,
47 + }}
48 key="drawer"
49 as={motion.div}
50 initial={{x: '100%'}}
51 animate={{x: 0}}
52 exit={{x: '100%'}}
53 transition={{type: 'tween', duration: 0.2}}
44 - width={300}
45 - top={0}
46 - right={0}
47 - bottom={0}
48 - bg="gray.0"
49 - style={{zIndex: 1}}
54 >
55 {children}
56 </Box>
src/components/head.js deleted
-27
@@ -1,27 +0,0 @@
1 -import React from 'react'
2 -import {Helmet} from 'react-helmet'
3 -import useSiteMetdata from '../hooks/use-site-metadata'
4 -import {useFrontmatter} from '../layout'
5 -
6 -function Head() {
7 - const fm = useFrontmatter()
8 - const siteMetadata = useSiteMetdata()
9 -
10 - const title = [fm.title, siteMetadata.title].filter(Boolean).join(' | ')
11 - const description = fm.description || siteMetadata.description
12 - const lang = fm.lang || siteMetadata.lang
13 -
14 - return (
15 - <Helmet>
16 - <title>{title}</title>
17 - <meta name="description" content={description} />
18 - <meta property="og:title" content={title} />
19 - <meta property="og:description" content={description} />
20 - <meta property="og:image" content={siteMetadata.imageUrl} />
21 - <meta property="twitter:card" content="summary_large_image" />
22 - <html lang={lang} />
23 - </Helmet>
24 - )
25 -}
26 -
27 -export default Head
src/components/header.js
+33 -18
@@ -5,54 +5,69 @@ import styled from 'styled-components'
5 import MobileSearch from './mobile-search'
6 import NavDrawer from './nav-drawer'
7 import Search from './search'
8 -import NpmLogo from './npm-logo'
8 import useSearch from '../hooks/use-search'
9 import useSiteMetadata from '../hooks/use-site-metadata'
10 import headerNavItems from '../header-nav.yml'
12 -import {HEADER_HEIGHT} from '../constants'
11 +import {HEADER_HEIGHT, NPM_RED} from '../constants'
12
13 const NpmHeaderBar = styled(Box)`
14 height: 10px;
15 background-image: linear-gradient(139deg, #fb8817, #ff4b01, #c12127, #e02aff);
16 `
17
18 +const NpmLogo = ({size, style}) => (
19 + <svg
20 + height={size}
21 + width={size}
22 + viewBox="0 0 700 700"
23 + fill="currentColor"
24 + style={{color: NPM_RED, ...style}}
25 + aria-hidden="true"
26 + >
27 + <polygon fill={NPM_RED} points="0,700 700,700 700,0 0,0" />
28 + <polygon fill="#ffffff" points="150,550 350,550 350,250 450,250 450,550 550,550 550,150 150,150 " />
29 + </svg>
30 +)
31 +
32 function Header() {
33 const siteMetadata = useSiteMetadata()
34 const search = useSearch()
35
36 return (
24 - <Box position="sticky" top={0} sx={{zIndex: 1}} role="banner">
37 + <Box sx={{top: 0, position: 'sticky', zIndex: 1}} role="banner">
38 <NpmHeaderBar />
39 <Box
27 - display="flex"
28 - height={HEADER_HEIGHT}
29 - px={[3, null, null, 4]}
30 - alignItems="center"
31 - justifyContent="space-between"
32 - bg="#333333"
40 + sx={{
41 + display: 'flex',
42 + height: HEADER_HEIGHT,
43 + px: [3, null, null, 4],
44 + alignItems: 'center',
45 + justifyContent: 'space-between',
46 + bg: '#333333',
47 + }}
48 >
34 - <Box display="flex" alignItems="center">
49 + <Box sx={{display: 'flex', alignItems: 'center'}}>
50 <Link
51 as={GatsbyLink}
52 to="/"
38 - style={{
53 + sx={{
54 + mr: 4,
55 color: '#dddddd',
56 fontWeight: '600',
57 display: 'flex',
58 alignItems: 'center',
59 }}
44 - sx={{mr: 4}}
60 >
46 - <NpmLogo size="32" style={{color: '#cb0000', marginRight: '16px'}} />
61 + <NpmLogo size="32" sx={{mr: '16px'}} />
62 {siteMetadata.title}
63 </Link>
49 - <Box display={['none', null, null, 'block']} ml={4}>
64 + <Box sx={{display: ['none', null, null, 'block'], ml: 4}}>
65 <Search {...search} />
66 </Box>
67 </Box>
53 - <Box display="flex">
54 - <Box display={['none', null, null, 'block']}>
55 - <Box display="flex" alignItems="center" color="gray.2">
68 + <Box sx={{display: 'flex'}}>
69 + <Box sx={{display: ['none', null, null, 'block']}}>
70 + <Box sx={{display: 'flex', alignItems: 'center', color: 'gray.2'}}>
71 {headerNavItems.map((item, index) => (
72 <Link key={index} href={item.url} sx={{display: 'block', color: 'inherit', ml: 4}}>
73 {item.title}
@@ -60,7 +75,7 @@ function Header() {
75 ))}
76 </Box>
77 </Box>
63 - <Box display={['flex', null, null, 'none']}>
78 + <Box sx={{display: ['flex', null, null, 'none']}}>
79 <MobileSearch {...search} />
80 <NavDrawer />
81 </Box>
src/components/mobile-search.js
+33 -24
@@ -1,9 +1,8 @@
1 import React from 'react'
2 -import {Box} from '@primer/react'
2 +import {Button, Box} from '@primer/react'
3 import {XIcon, SearchIcon} from '@primer/octicons-react'
4 import {AnimatePresence, motion} from 'framer-motion'
5 import {FocusOn} from 'react-focus-on'
6 -import DarkButton from './dark-button'
6 import DarkTextInput from './dark-text-input'
7 import SearchResults from './search-results'
8 import useSiteMetadata from '../hooks/use-site-metadata'
@@ -19,23 +18,34 @@ function MobileSearch({onDismiss, ...props}) {
18
19 return (
20 <FocusOn returnFocus={true} onEscapeKey={handleDismiss}>
22 - <Box position="fixed" top="10px" left={0} right={0} bottom={0} zIndex={1}>
21 + <Box
22 + sx={{
23 + position: 'fixed',
24 + top: '10px',
25 + left: 0,
26 + right: 0,
27 + bottom: 0,
28 + zIndex: 1,
29 + }}
30 + >
31 <Box
24 - position="absolute"
32 + sx={{
33 + position: 'absolute',
34 + top: 0,
35 + left: 0,
36 + right: 0,
37 + bottom: 0,
38 + bg: 'rgba(0,0,0,0.5)',
39 + zIndex: -1,
40 + }}
41 as={motion.div}
42 initial={{opacity: 0}}
43 animate={{opacity: 1}}
44 exit={{opacity: 0}}
29 - top={0}
30 - left={0}
31 - right={0}
32 - bottom={0}
33 - bg="rgba(0,0,0,0.5)"
34 - zIndex={-1}
45 onClick={handleDismiss}
46 />
37 - <Box display="flex" flexDirection="column" height={isOpen ? '100%' : 'auto'}>
38 - <Box display="flex" bg="gray.9" color="white" p={3} flex="0 0 auto">
47 + <Box sx={{display: 'flex', flexDirection: 'column', height: isOpen ? '100%' : 'auto'}}>
48 + <Box sx={{display: 'flex', bg: 'gray.9', color: 'white', p: 3, flex: '0 0 auto'}}>
49 <motion.div
50 initial={{scaleX: 0.1}}
51 animate={{scaleX: 1}}
@@ -46,27 +56,26 @@ function MobileSearch({onDismiss, ...props}) {
56 <DarkTextInput
57 {...getInputProps({
58 placeholder: `Search ${siteMetadata.title}`,
49 - width: '100%',
59 'aria-label': `Search ${siteMetadata.title}`,
60 + width: '100%',
61 })}
62 />
63 </motion.div>
54 - <DarkButton ml={3} aria-label="Cancel" onClick={handleDismiss}>
64 + <Button sx={{ml: 3}} aria-label="Cancel" onClick={handleDismiss}>
65 <XIcon />
56 - </DarkButton>
66 + </Button>
67 </Box>
68 <Box
59 - display="flex"
60 - {...getMenuProps({
69 + {...getMenuProps()}
70 + sx={{
71 + display: 'flex',
72 bg: 'white',
73 py: isOpen ? 1 : 0,
74 flexDirection: 'column',
75 flex: '1 1 auto',
65 - style: {
66 - overflow: 'auto',
67 - WebkitOverflowScrolling: 'touch',
68 - },
69 - })}
76 + overflow: 'auto',
77 + WebkitOverflowScrolling: 'touch',
78 + }}
79 >
80 {isOpen ? <SearchResults {...{results, getItemProps, highlightedIndex}} /> : null}
81 </Box>
@@ -81,9 +90,9 @@ function MobileSearchWrapper(props) {
90
91 return (
92 <>
84 - <DarkButton aria-label="Search" aria-expanded={isOpen} onClick={() => setIsOpen(true)}>
93 + <Button aria-label="Search" aria-expanded={isOpen} onClick={() => setIsOpen(true)}>
94 <SearchIcon />
86 - </DarkButton>
95 + </Button>
96 <AnimatePresence>
97 {isOpen ? <MobileSearch onDismiss={() => setIsOpen(false)} {...props} /> : null}
98 </AnimatePresence>
src/components/nav-drawer.js
+38 -31
@@ -1,11 +1,9 @@
1 import React from 'react'
2 -import {Box, Link} from '@primer/react'
2 +import {Button, Box, Link} from '@primer/react'
3 import {XIcon, ThreeBarsIcon} from '@primer/octicons-react'
4 import {Link as GatsbyLink} from 'gatsby'
5 -import DarkButton from './dark-button'
5 import Drawer from './drawer'
6 import NavItems from './nav-items'
8 -import navItems from '../nav.yml'
7 import headerNavItems from '../header-nav.yml'
8 import useSiteMetadata from '../hooks/use-site-metadata'
9 import {useIsMobile} from '../hooks/use-breakpoint'
@@ -31,51 +29,60 @@ function NavDrawer() {
29
30 return (
31 <>
34 - <DarkButton aria-label="Menu" aria-expanded={isOpen} onClick={setOpen} ml={3}>
32 + <Button aria-label="Menu" aria-expanded={isOpen} onClick={setOpen} sx={{ml: 3}}>
33 <ThreeBarsIcon />
36 - </DarkButton>
34 + </Button>
35 <Drawer isOpen={isOpen} onDismiss={setClose}>
36 <Box
39 - display="flex"
40 - flexDirection="column"
41 - height="100%"
42 - bg="gray.0"
43 - style={{overflow: 'auto', WebkitOverflowScrolling: 'touch'}}
37 + sx={{
38 + display: 'flex',
39 + flexDirection: 'column',
40 + height: '100%',
41 + bg: 'gray.0',
42 + overflow: 'auto',
43 + WebkitOverflowScrolling: 'touch',
44 + }}
45 >
45 - <Box display="flex" flexDirection="column" flex="1 0 auto" color="gray.7" bg="gray.0">
46 - <Box borderStyle="solid" borderWidth={0} borderRadius={0} borderBottomWidth={1} borderColor="gray.7">
46 + <Box sx={{display: 'flex', flexDirection: 'column', flex: '1 0 auto', color: 'gray.7', bg: 'gray.0'}}>
47 + <Box
48 + sx={{borderStyle: 'solid', borderWidth: 0, borderRadius: 0, borderBottomWidth: 1, borderColor: 'gray.7'}}
49 + >
50 <Box
48 - display="flex"
49 - py={3}
50 - pl={4}
51 - pr={3}
52 - alignItems="center"
53 - justifyContent="space-between"
54 - color="gray.1"
55 - bg="gray.9"
51 + sx={{
52 + display: 'flex',
53 + py: 3,
54 + pl: 4,
55 + pr: 3,
56 + alignItems: 'center',
57 + justifyContent: 'space-between',
58 + color: 'gray.1',
59 + bg: 'gray.9',
60 + }}
61 >
62 <Link as={GatsbyLink} to="/" sx={{display: 'inline-block', color: 'inherit'}}>
63 {siteMetadata.title}
64 </Link>
60 - <DarkButton aria-label="Close" onClick={setClose}>
65 + <Button aria-label="Close" onClick={setClose}>
66 <XIcon />
62 - </DarkButton>
67 + </Button>
68 </Box>
69 </Box>
65 - <Box display="flex" flexDirection="column">
66 - <NavItems items={navItems} />
70 + <Box sx={{display: 'flex', flexDirection: 'column'}}>
71 + <NavItems />
72 </Box>
73 </Box>
69 - <Box display="flex" flexDirection="column" flex="1 0 auto" color="gray.1" bg="gray.9">
74 + <Box sx={{display: 'flex', flexDirection: 'column', flex: '1 0 auto', color: 'gray.1', bg: 'gray.9'}}>
75 {headerNavItems.map((item, index) => (
76 <Box
72 - borderStyle="solid"
77 key={item.title}
74 - borderWidth={0}
75 - borderRadius={0}
76 - borderTopWidth={index !== 0 ? 1 : 0}
77 - borderColor="gray.7"
78 - p={4}
78 + sx={{
79 + borderStyle: 'solid',
80 + borderWidth: 0,
81 + borderRadius: 0,
82 + borderTopWidth: index !== 0 ? 1 : 0,
83 + borderColor: 'gray.7',
84 + p: 4,
85 + }}
86 >
87 <Link key={index} href={item.url} sx={{color: 'inherit', display: 'block'}}>
88 {item.title}
src/components/nav-items.js
+80 -95
@@ -1,6 +1,6 @@
1 import React from 'react'
2 import {Link as GatsbyLink} from 'gatsby'
3 -import {Box, StyledOcticon, Link, themeGet} from '@primer/react'
3 +import {Box, Octicon, Link, themeGet} from '@primer/react'
4 import {LinkExternalIcon} from '@primer/octicons-react'
5 import styled from 'styled-components'
6 import getNav from '../util/get-nav'
@@ -18,6 +18,19 @@ const ActiveLink = ({className, children, ...props}) => (
18 </Link>
19 )
20
21 +const withItems = Component => {
22 + const WithItems = ({parent, path}) => {
23 + if (!parent || getNav.isActiveUrl(path, parent.url)) {
24 + const items = getNav.getHierarchy(parent, {path, hideVariants: true})
25 + if (items) {
26 + return <Component items={items} path={path} />
27 + }
28 + }
29 + return null
30 + }
31 + return WithItems
32 +}
33 +
34 const NavLink = styled(ActiveLink)`
35 color: inherit;
36 text-decoration: none;
@@ -35,6 +48,34 @@ const TopLevelLink = styled(NavLink)`
48 color: ${themeGet('colors.gray.8')};
49 }
50 `
51 +
52 +const TopLevelItems = withItems(({items, path}) => (
53 + <>
54 + {items.map(item => (
55 + <Box
56 + key={item.title}
57 + role="listitem"
58 + sx={{
59 + borderStyle: 'solid',
60 + borderColor: 'border.default',
61 + borderWidth: 0,
62 + borderRadius: 0,
63 + borderTopWidth: 1,
64 + py: 3,
65 + px: 4,
66 + }}
67 + >
68 + <Box sx={{display: 'flex', flexDirection: 'column'}}>
69 + <TopLevelLink to={item.url} key={item.title}>
70 + {item.title}
71 + </TopLevelLink>
72 + <SecondLevelItems parent={item} path={path} />
73 + </Box>
74 + </Box>
75 + ))}
76 + </>
77 +))
78 +
79 const SecondLevelLink = styled(NavLink)`
80 display: block;
81 font-size: ${themeGet('fontSizes.1')};
@@ -47,6 +88,28 @@ const SecondLevelLink = styled(NavLink)`
88 }
89 `
90
91 +const Description = styled(Box)`
92 + & {
93 + color: ${themeGet('colors.gray.6')};
94 + font-size: 0.8em;
95 + font-weight: normal;
96 + }
97 +`
98 +
99 +const SecondLevelItems = withItems(({items, path}) => (
100 + <Box sx={{display: 'flex', flexDirection: 'column', mt: 2}} role="list">
101 + {items.map(item => (
102 + <Box key={item.title} role="listitem">
103 + <SecondLevelLink key={item.url} to={item.url}>
104 + {item.title}
105 + {item.description ? <Description>{item.description}</Description> : null}
106 + </SecondLevelLink>
107 + <ThirdLevelItems parent={item} path={path} />
108 + </Box>
109 + ))}
110 + </Box>
111 +))
112 +
113 const ThirdLevelLink = styled(NavLink)`
114 display: block;
115 font-size: ${themeGet('fontSizes.1')};
@@ -62,108 +125,30 @@ const ThirdLevelLink = styled(NavLink)`
125 }
126 `
127
65 -const Description = styled(Box)`
66 - & {
67 - color: ${themeGet('colors.gray.6')};
68 - font-size: 0.8em;
69 - font-weight: normal;
70 - }
71 -`
72 -
73 -function topLevelItems(items, path) {
74 - if (items == null) {
75 - return null
76 - }
77 -
78 - return (
79 - <>
80 - {items.map(item => {
81 - const children = getNav.isActiveUrl(path, item.url)
82 - ? getNav.getHierarchy(item, {path, hideVariants: true})
83 - : null
84 -
85 - return (
86 - <Box
87 - borderStyle="solid"
88 - borderColor="border.default"
89 - key={item.title}
90 - borderWidth={0}
91 - borderRadius={0}
92 - borderTopWidth={1}
93 - py={3}
94 - px={4}
95 - role="listitem"
96 - >
97 - <Box display="flex" flexDirection="column">
98 - <TopLevelLink to={item.url} key={item.title}>
99 - {item.title}
100 - </TopLevelLink>
101 - {secondLevelItems(children, path)}
102 - </Box>
103 - </Box>
104 - )
105 - })}
106 - </>
107 - )
108 -}
109 -
110 -function secondLevelItems(items, path) {
111 - if (items == null) {
112 - return null
113 - }
114 -
115 - return (
116 - <Box display="flex" flexDirection="column" mt={2} role="list">
117 - {items.map(item => {
118 - const children = getNav.isActiveUrl(path, item.url)
119 - ? getNav.getHierarchy(item, {path, hideVariants: true})
120 - : null
121 - return (
122 - <Box key={item.title} role="listitem">
123 - <SecondLevelLink key={item.url} to={item.url}>
124 - {item.title}
125 - {item.description != null ? <Description>{item.description}</Description> : null}
126 - </SecondLevelLink>
127 - {thirdLevelItems(children, path)}
128 - </Box>
129 - )
130 - })}
131 - </Box>
132 - )
133 -}
134 -
135 -function thirdLevelItems(items) {
136 - if (items == null) {
137 - return null
138 - }
139 -
140 - return (
141 - <Box display="flex" flexDirection="column" mt={2} role="list">
142 - {items.map(item => (
143 - <Box key={item.title} role="listitem">
144 - <ThirdLevelLink key={item.url} to={item.url}>
145 - {item.title}
146 - </ThirdLevelLink>
147 - </Box>
148 - ))}
149 - </Box>
150 - )
151 -}
128 +const ThirdLevelItems = withItems(({items}) => (
129 + <Box sx={{display: 'flex', flexDirection: 'column', mt: 2}} role="list">
130 + {items.map(item => (
131 + <Box key={item.title} role="listitem">
132 + <ThirdLevelLink key={item.url} to={item.url}>
133 + {item.title}
134 + </ThirdLevelLink>
135 + </Box>
136 + ))}
137 + </Box>
138 +))
139
140 function NavItems() {
154 - const location = useLocation()
141 const {repositoryUrl} = usePageContext()
156 - const path = getNav.getLocation(location.pathname)
157 - const items = getNav.getHierarchy(null, {path, hideVariants: true})
142 + const location = useLocation()
143
144 return (
145 <>
161 - {topLevelItems(items, path)}
162 - <Box borderStyle="solid" borderColor="border.default" borderWidth={0} borderTopWidth={1} py={3} px={4}>
146 + <TopLevelItems path={getNav.getLocation(location.pathname)} />
147 + <Box sx={{borderStyle: 'solid', borderColor: 'border.default', borderWidth: 0, borderTopWidth: 1, py: 3, px: 4}}>
148 <Link href={repositoryUrl} sx={{color: 'inherit'}}>
164 - <Box display="flex" justifyContent="space-between" alignItems="center" color="gray.5">
149 + <Box sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', color: 'gray.5'}}>
150 GitHub
166 - <StyledOcticon icon={LinkExternalIcon} color="gray.5" />
151 + <Octicon icon={LinkExternalIcon} sx={{color: 'gray.5'}} />
152 </Box>
153 </Link>
154 </Box>
src/components/npm-logo.js deleted
-12
@@ -1,12 +0,0 @@
1 -import React from 'react'
2 -
3 -const NpmLogo = ({size, style}) => {
4 - return (
5 - <svg height={size} width={size} viewBox="0 0 700 700" fill="currentColor" style={style} aria-hidden="true">
6 - <polygon fill="#cb0000" points="0,700 700,700 700,0 0,0" />
7 - <polygon fill="#ffffff" points="150,550 350,550 350,250 450,250 450,550 550,550 550,150 150,150 " />
8 - </svg>
9 - )
10 -}
11 -
12 -export default NpmLogo
src/components/page-footer.js
+12 -10
@@ -7,18 +7,20 @@ function PageFooter({editUrl, contributors = {}}) {
7 const {logins = [], latestCommit} = contributors
8 return editUrl || logins.length ? (
9 <Box
10 - borderStyle="solid"
11 - borderColor="border.default"
12 - borderRadius={2}
13 - borderWidth={0}
14 - borderTopWidth={1}
15 - mt={8}
16 - py={5}
10 + sx={{
11 + borderStyle: 'solid',
12 + borderColor: 'border.default',
13 + borderRadius: 2,
14 + borderWidth: 0,
15 + borderTopWidth: 1,
16 + mt: 8,
17 + py: 5,
18 + }}
19 >
18 - <Box display="grid" gridGap={4}>
19 - {editUrl != null ? (
20 + <Box sx={{display: 'grid', gap: 4}}>
21 + {editUrl ? (
22 <Link href={editUrl}>
21 - <Octicon icon={PencilIcon} mr={2} />
23 + <Octicon icon={PencilIcon} sx={{mr: 2}} />
24 Edit this page on GitHub
25 </Link>
26 ) : null}
src/components/search-results.js
+20 -21
@@ -3,12 +3,24 @@ import {Box, Text} from '@primer/react'
3 import useSiteMetadata from '../hooks/use-site-metadata'
4 import getNav from '../util/get-nav'
5
6 -function SearchResults({results, getItemProps, highlightedIndex}) {
6 +const Breadcrumbs = ({item, highlighted}) => {
7 const siteMetadata = useSiteMetadata()
8 + const hierarchy = getNav.getItemHierarchy(item.path)
9 +
10 + const text = hierarchy
11 + ? hierarchy
12 + .slice(0, -1)
13 + .map(item => item.shortName || item.title)
14 + .join(' / ')
15 + : siteMetadata.shortName
16
17 + return <Text sx={{fontSize: 0, color: highlighted ? 'blue.2' : 'gray.7'}}>{text}</Text>
18 +}
19 +
20 +function SearchResults({results, getItemProps, highlightedIndex}) {
21 if (results.length === 0) {
22 return (
11 - <Text as="div" p={3} fontSize={1} color="gray.7" width="100%">
23 + <Text as="div" sx={{p: 3, fontSize: 1, color: 'gray.7', width: '100%'}}>
24 No results
25 </Text>
26 )
@@ -16,36 +28,23 @@ function SearchResults({results, getItemProps, highlightedIndex}) {
28
29 return results.map((item, index) => (
30 <Box
19 - display="flex"
31 key={item.path}
21 - {...getItemProps({
22 - item,
32 + sx={{
33 + display: 'flex',
34 flexDirection: 'column',
35 flex: '0 0 auto',
36 px: 3,
37 py: 2,
38 color: highlightedIndex === index ? 'white' : 'gray.8',
39 bg: highlightedIndex === index ? 'blue.5' : 'transparent',
29 - style: {cursor: 'pointer'},
30 - })}
40 + cursor: 'pointer',
41 + }}
42 + {...getItemProps({item})}
43 >
32 - <Text fontSize={0} color={highlightedIndex === index ? 'blue.2' : 'gray.7'}>
33 - {getBreadcrumbs(siteMetadata.shortName, item.path).join(' / ')}
34 - </Text>
44 + <Breadcrumbs item={item} highlighted={highlightedIndex === index} />
45 {item.title}
46 </Box>
47 ))
48 }
49
40 -function getBreadcrumbs(siteTitle, path) {
41 - const hierarchy = getNav.getItemHierarchy(path)
42 -
43 - if (hierarchy) {
44 - hierarchy.pop()
45 - return hierarchy.map(item => (item.shortName ? item.shortName : item.title))
46 - } else {
47 - return [siteTitle]
48 - }
49 -}
50 -
50 export default SearchResults
src/components/search.js
+14 -12
@@ -9,11 +9,11 @@ function Search(props) {
9 const {getInputProps, getMenuProps, isOpen, results, getItemProps, highlightedIndex} = props
10
11 return (
12 - <Box position="relative">
12 + <Box sx={{position: 'relative'}}>
13 <DarkTextInput
14 + sx={{width: '240px'}}
15 {...getInputProps({
16 placeholder: `Search ${siteMetadata.title}`,
16 - width: 240,
17 'aria-label': `Search ${siteMetadata.title}`,
18 })}
19 />
@@ -27,16 +27,18 @@ function Search(props) {
27 >
28 {isOpen ? (
29 <Box
30 - borderWidth={1}
31 - borderStyle="solid"
32 - borderColor="border.default"
33 - borderRadius={2}
34 - minWidth={300}
35 - maxHeight="70vh"
36 - py={1}
37 - boxShadow="medium"
38 - bg="white"
39 - style={{overflow: 'auto'}}
30 + sx={{
31 + borderWidth: 1,
32 + borderStyle: 'solid',
33 + borderColor: 'border.default',
34 + borderRadius: 2,
35 + minWidth: '300px',
36 + maxHeight: '70vh',
37 + py: 1,
38 + boxShadow: 'medium',
39 + bg: 'white',
40 + overflow: 'auto',
41 + }}
42 >
43 <SearchResults {...{results, getItemProps, highlightedIndex}} />
44 </Box>
src/components/sidebar.js
+19 -16
@@ -1,30 +1,33 @@
1 import {Box} from '@primer/react'
2 import React from 'react'
3 import NavItems from './nav-items'
4 -import navItems from '../nav.yml'
4 import {HEADER_HEIGHT} from '../constants'
5
6 const Sidebar = () => (
7 <Box
9 - position="sticky"
10 - top={HEADER_HEIGHT}
11 - height={`calc(100vh - ${HEADER_HEIGHT}px)`}
12 - minWidth={260}
13 - color="gray.8"
14 - bg="gray.0"
8 role="navigation"
9 + sx={{
10 + position: 'sticky',
11 + top: `${HEADER_HEIGHT}px`,
12 + height: `calc(100vh - ${HEADER_HEIGHT}px)`,
13 + minWidth: 260,
14 + color: 'gray.8',
15 + bg: 'gray.0',
16 + }}
17 >
18 <Box
18 - borderStyle="solid"
19 - borderColor="border.default"
20 - borderWidth={0}
21 - borderRightWidth={1}
22 - borderRadius={0}
23 - height="100%"
24 - style={{overflow: 'auto'}}
19 + sx={{
20 + borderStyle: 'solid',
21 + borderColor: 'border.default',
22 + borderWidth: 0,
23 + borderRightWidth: 1,
24 + borderRadius: 0,
25 + height: '100%',
26 + overflow: 'auto',
27 + }}
28 >
26 - <Box display="flex" flexDirection="column" role="list">
27 - <NavItems items={navItems} />
29 + <Box sx={{display: 'flex', flexDirection: 'column'}} role="list">
30 + <NavItems />
31 </Box>
32 </Box>
33 </Box>
src/components/table-of-contents.js
+37 -40
@@ -1,5 +1,5 @@
1 import React from 'react'
2 -import {Box, Link, Text, Octicon, Details} from '@primer/react'
2 +import {Box, Link, Text, Octicon, Details, useDetails} from '@primer/react'
3 import {ChevronDownIcon, ChevronRightIcon} from '@primer/octicons-react'
4 import {usePageContext} from '../layout'
5 import {HEADER_HEIGHT} from '../constants'
@@ -9,13 +9,11 @@ const TableOfContents = ({items, depth = 0, labelId}) => (
9 key={items}
10 as="ul"
11 role="list"
12 - m={0}
13 - p={0}
14 - css={{listStyle: 'none', lineHeight: '1.4em'}}
12 + sx={{m: 0, p: 0, listStyle: 'none', lineHeight: '1.4em'}}
13 aria-labelledby={labelId}
14 >
15 {items.map(item => (
18 - <Box as="li" role="listitem" key={item.url} pl={depth > 0 ? 3 : 0}>
16 + <Box as="li" role="listitem" key={item.url} sx={{pl: depth > 0 ? 3 : 0}}>
17 <Link key={item.title} href={item.url} sx={{display: 'inline-block', py: 1, color: 'gray.6'}}>
18 {item.title}
19 </Link>
@@ -25,48 +23,47 @@ const TableOfContents = ({items, depth = 0, labelId}) => (
23 </Box>
24 )
25
28 -const withTableOfContent = Component => {
29 - const TOC = props => {
26 +const withTableOfContents = Component => {
27 + const WithTableOfContents = props => {
28 const {tableOfContents} = usePageContext()
31 - if (!tableOfContents) {
32 - return null
33 - }
34 - return <Component {...props} items={tableOfContents} />
29 + return tableOfContents ? <Component {...props} items={tableOfContents} /> : null
30 }
36 - return TOC
31 + return WithTableOfContents
32 }
33
39 -export const Mobile = withTableOfContent(({items}) => (
40 - <Box display={['block', null, 'none']} mb={3}>
41 - <Details>
42 - {({open}) => (
43 - <>
44 - <Text as="summary" fontWeight="bold">
45 - <Octicon icon={open ? ChevronDownIcon : ChevronRightIcon} mr={2} />
46 - Table of contents
47 - </Text>
48 - <Box pt={1}>
49 - <TableOfContents items={items} />
50 - </Box>
51 - </>
52 - )}
53 - </Details>
54 - </Box>
55 -))
34 +export const Mobile = withTableOfContents(({items}) => {
35 + const {getDetailsProps, open} = useDetails({})
36 + return (
37 + <Box sx={{display: ['block', null, 'none'], mb: 3}}>
38 + <Details {...getDetailsProps()}>
39 + <Text as="summary" sx={{fontWeight: 'bold'}}>
40 + <Octicon icon={open ? ChevronDownIcon : ChevronRightIcon} sx={{mr: 2}} />
41 + Table of contents
42 + </Text>
43 + <Box sx={{pt: 1}}>
44 + <TableOfContents items={items} />
45 + </Box>
46 + </Details>
47 + </Box>
48 + )
49 +})
50
57 -export const Desktop = withTableOfContent(({items}) => (
51 +export const Desktop = withTableOfContents(({items}) => (
52 <Box
59 - display={['none', null, 'block']}
60 - css={{gridArea: 'table-of-contents', overflow: 'auto'}}
61 - position="sticky"
62 - top={HEADER_HEIGHT + 24}
63 - mt="6px"
64 - maxHeight={`calc(100vh - ${HEADER_HEIGHT}px - 24px)`}
65 - pr={1}
66 - pl={1}
67 - pb={1}
53 + sx={{
54 + display: ['none', null, 'block'],
55 + position: 'sticky',
56 + top: `${HEADER_HEIGHT + 24}px`,
57 + maxHeight: `calc(100vh - ${HEADER_HEIGHT}px - 24px)`,
58 + mt: '6px',
59 + pr: 1,
60 + pl: 1,
61 + pb: 1,
62 + gridArea: 'table-of-contents',
63 + overflow: 'auto',
64 + }}
65 >
69 - <Text display="inline-block" fontWeight="bold" mb={1} id="table-of-content-label">
66 + <Text id="table-of-content-label" sx={{display: 'inline-block', fontWeight: 'bold', mb: 1}}>
67 Table of contents
68 </Text>
69 <TableOfContents items={items} labelId="table-of-content-label" />
src/constants.js
+2
@@ -1,3 +1,5 @@
1 export const HEADER_HEIGHT = 66
2
3 export const SKIP_NAV = 'skip-nav'
4 +
5 +export const NPM_RED = '#cb0000'
src/layout.js
+27 -6
@@ -1,10 +1,11 @@
1 import React from 'react'
2 +import {Helmet} from 'react-helmet'
3 import {Box} from '@primer/react'
4 import Slugger from 'github-slugger'
4 -import Head from './components/head'
5 import Header from './components/header'
6 import Sidebar from './components/sidebar'
7 import {SKIP_NAV} from './constants'
8 +import useSiteMetdata from './hooks/use-site-metadata'
9
10 const SluggerContext = React.createContext(null)
11 const PageContext = React.createContext(null)
@@ -13,21 +14,41 @@ const LocationContext = React.createContext(null)
14 export const useSlugger = () => React.useContext(SluggerContext)
15 export const usePageContext = () => React.useContext(PageContext)
16 export const useLocation = () => React.useContext(LocationContext)
16 -export const useFrontmatter = () => usePageContext().frontmatter
17 +
18 +const Head = () => {
19 + const {frontmatter} = usePageContext()
20 + const siteMetadata = useSiteMetdata()
21 +
22 + const title = [frontmatter.title, siteMetadata.title].filter(Boolean).join(' | ')
23 + const description = frontmatter.description || siteMetadata.description
24 + const lang = frontmatter.lang || siteMetadata.lang
25 +
26 + return (
27 + <Helmet>
28 + <title>{title}</title>
29 + <meta name="description" content={description} />
30 + <meta property="og:title" content={title} />
31 + <meta property="og:description" content={description} />
32 + <meta property="og:image" content={siteMetadata.imageUrl} />
33 + <meta property="twitter:card" content="summary_large_image" />
34 + <html lang={lang} />
35 + </Helmet>
36 + )
37 +}
38
39 const withLayout = Component => {
40 const LayoutProvider = props => (
41 <SluggerContext.Provider value={new Slugger()}>
42 <PageContext.Provider value={props.pageContext}>
43 <LocationContext.Provider value={props.location}>
23 - <Box display="flex" flexDirection="column" minHeight="100vh">
44 + <Box sx={{display: 'flex', flexDirection: 'column', minHeight: '100vh'}}>
45 <Head />
46 <Header />
26 - <Box display="flex" flex="1 1 auto" flexDirection="row" css={{zIndex: 0}} role="main">
27 - <Box display={['none', null, null, 'block']}>
47 + <Box sx={{display: 'flex', flex: '1 1 auto', flexDirection: 'row', zIndex: 0}} role="main">
48 + <Box sx={{display: ['none', null, null, 'block']}}>
49 <Sidebar />
50 </Box>
30 - <Box id={SKIP_NAV}>
51 + <Box sx={{width: '100%'}} id={SKIP_NAV}>
52 <Component {...props} />
53 </Box>
54 </Box>
src/layout/default.js
+20 -15
@@ -10,25 +10,30 @@ const Layout = ({children, pageContext: {frontmatter}}) => {
10
11 return (
12 <Box
13 - display="grid"
14 - maxWidth="100%"
15 - gridTemplateColumns={['100%', null, 'minmax(0, 65ch) 220px']}
16 - gridTemplateAreas={['"heading" "content"', null, '"heading table-of-contents" "content table-of-contents"']}
17 - gridColumnGap={[null, null, 6, 7]}
18 - gridRowGap={3}
19 - mx="auto"
20 - p={[5, 6, null, 7]}
21 - css={{alignItems: 'start', alignSelf: 'start'}}
13 role="region"
14 + sx={{
15 + display: 'grid',
16 + maxWidth: '100%',
17 + gridTemplateColumns: ['100%', null, 'minmax(0, 65ch) 220px'],
18 + gridTemplateAreas: ['"heading" "content"', null, '"heading table-of-contents" "content table-of-contents"'],
19 + columnGap: [null, null, 6, 7],
20 + rowGap: 3,
21 + mx: 'auto',
22 + p: [5, 6, null, 7],
23 + alignItems: 'start',
24 + alignSelf: 'start',
25 + }}
26 >
27 <Box css={{gridArea: 'heading'}}>
28 <Box
26 - borderStyle="solid"
27 - borderColor="border.default"
28 - borderWidth={0}
29 - borderBottomWidth={1}
30 - borderRadius={0}
31 - pb={2}
29 + sx={{
30 + borderStyle: 'solid',
31 + borderColor: 'border.default',
32 + borderWidth: 0,
33 + borderBottomWidth: 1,
34 + borderRadius: 0,
35 + pb: 2,
36 + }}
37 >
38 <Heading as="h1">{title}</Heading>
39 {description}
src/layout/hero.js
+1 -1
@@ -5,7 +5,7 @@ import Hero from '../components/hero'
5 import withLayout from '../layout'
6
7 const HeroLayout = ({children}) => (
8 - <Box width="100%">
8 + <Box sx={{width: '100%'}}>
9 <Hero />
10 <Container>{children}</Container>
11 </Box>
src/mdx/code.js
+13 -11
@@ -10,9 +10,9 @@ function Code({className: language = '', children}) {
10 const {scrollRef, paddingRef, size} = useScrollSize()
11
12 return (
13 - <Box position="relative">
13 + <Box sx={{position: 'relative'}}>
14 <div ref={paddingRef}>
15 - <Box position="absolute" top={0} right={0} p={2} zIndex={1}>
15 + <Box sx={{position: 'absolute', top: 0, right: 0, p: 2, zIndex: 1}}>
16 <ClipboardCopy value={code} />
17 </Box>
18 </div>
@@ -20,16 +20,18 @@ function Code({className: language = '', children}) {
20 {({className, style, tokens, getLineProps, getTokenProps}) => (
21 <Box
22 tabIndex={0}
23 - borderWidth={1}
24 - borderStyle="solid"
25 - borderColor="border.default"
26 - borderRadius={2}
23 as="pre"
24 className={className}
29 - mt={0}
30 - mb={3}
31 - p={3}
32 - border={0}
25 + sx={{
26 + borderWidth: 1,
27 + borderStyle: 'solid',
28 + borderColor: 'border.default',
29 + borderRadius: 2,
30 + mt: 0,
31 + mb: 3,
32 + p: 3,
33 + border: 0,
34 + }}
35 style={{...style, overflow: 'auto'}}
36 >
37 {/* This is the scroll handle, it is supposed to be focused with keyboard and scroll a wide codebox horizontally */}
@@ -37,7 +39,7 @@ function Code({className: language = '', children}) {
39 {tokens.map((line, i) => (
40 <div key={i} {...getLineProps({line, key: i})}>
41 {line.map((token, key) => (
40 - <Text key={key} fontFamily="mono" fontSize={1} {...getTokenProps({token, key})} />
42 + <Text key={key} sx={{fontFamily: 'mono', fontSize: 1}} {...getTokenProps({token, key})} />
43 ))}
44 </div>
45 ))}
src/mdx/index.js
+16 -16
@@ -232,14 +232,16 @@ export const Table = styled.table`
232
233 export const Note = ({children}) => (
234 <Box
235 - borderWidth={1}
236 - borderStyle="solid"
237 - borderRadius={0}
238 - borderColor="blue.1"
239 - backgroundColor="blue.0"
240 - px={3}
241 - py={2}
242 - my={4}
235 + sx={{
236 + borderWidth: 1,
237 + borderStyle: 'solid',
238 + borderRadius: 0,
239 + borderColor: 'blue.1',
240 + backgroundColor: 'blue.0',
241 + px: 3,
242 + py: 2,
243 + my: 4,
244 + }}
245 >
246 {React.Children.toArray(children).map((child, index, list) =>
247 React.cloneElement(child, {
@@ -252,19 +254,17 @@ export const Note = ({children}) => (
254 export const Prompt = ({children}) => (
255 <Box
256 as="pre"
255 - mt={0}
256 - mb={3}
257 - p={3}
258 - border={0}
259 - style={{
257 + sx={{
258 + mt: 0,
259 + mb: 3,
260 + p: 3,
261 + border: 0,
262 color: 'rgb(57, 58, 52)',
263 backgroundColor: 'rgb(246, 248, 250)',
264 overflow: 'auto',
265 }}
266 >
265 - <Text fontFamily="mono" fontSize={1}>
266 - {children}
267 - </Text>
267 + <Text sx={{fontFamily: 'mono', fontSize: 1}}>{children}</Text>
268 </Box>
269 )
270
src/mdx/nav-hierarchy.js
+1 -3
@@ -12,9 +12,7 @@ const HierarchyItem = ({item, currentDepth, ...props}) => {
12 <Link as={GatsbyLink} key={item.title} to={item.url}>
13 {item.title}
14 </Link>
15 - {item.description != null ? (
16 - <Box style={{fontSize: '0.85em', marginBottom: '0.5em'}}>{item.description}</Box>
17 - ) : null}
15 + {item.description ? <Box style={{fontSize: '0.85em', marginBottom: '0.5em'}}>{item.description}</Box> : null}
16 {hierarchy ? <Hierarchy items={hierarchy} currentDepth={currentDepth + 1} {...props} /> : null}
17 </Box>
18 )
src/util/get-nav.js
+5 -8
@@ -144,14 +144,11 @@ const NavHierarchy = {
144
145 for (const item of items) {
146 if (item.variants) {
147 - const cloned = {}
148 - Object.assign(cloned, item)
149 -
150 - const variant = this.getCurrentOrDefaultVariant(item, props.path)
151 -
152 - cloned.url = variant.url
153 -
154 - updated.push(cloned)
147 + const {url} = this.getCurrentOrDefaultVariant(item, props.path)
148 + updated.push({
149 + ...item,
150 + url,
151 + })
152 } else {
153 updated.push(item)
154 }