mirror of
https://github.com/primer/css.git
synced 2024-09-11 16:36:07 +03:00
use blueprints!
This commit is contained in:
parent
c501c50b19
commit
2bbe22661d
@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"plugin:github/react",
|
||||
"plugin:jsx-a11y/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"import/no-namespace": 0,
|
||||
"no-unused-vars": ["error", {
|
||||
"ignoreRestSiblings": true
|
||||
}]
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import {Box} from '@primer/components'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const BoxShadow = styled(Box)`
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
`
|
||||
|
||||
export default BoxShadow
|
@ -1,47 +0,0 @@
|
||||
import React from 'react'
|
||||
import {findDOMNode} from 'react-dom'
|
||||
import {Button} from '@primer/components'
|
||||
import Octicon, {Clippy} from '@githubprimer/octicons-react'
|
||||
|
||||
export default class ClipboardCopy extends React.Component {
|
||||
copy() {
|
||||
const {value = ''} = this.props
|
||||
const {clipboard} = window.navigator
|
||||
const done = () => {
|
||||
// eslint-disable-next-line react/no-find-dom-node
|
||||
findDOMNode(this).dispatchEvent(new CustomEvent('copy', {bubbles: false}))
|
||||
}
|
||||
if (clipboard) {
|
||||
return clipboard.writeText(value).then(done)
|
||||
} else if (!document.body) {
|
||||
return
|
||||
} else {
|
||||
const node = document.createElement('pre')
|
||||
node.style.width = '1px'
|
||||
node.style.height = '1px'
|
||||
node.style.position = 'fixed'
|
||||
node.style.top = '5px'
|
||||
node.textContent = value
|
||||
|
||||
const selection = window.getSelection()
|
||||
document.body.appendChild(node)
|
||||
selection.removeAllRanges()
|
||||
const range = document.createRange()
|
||||
range.selectNodeContents(node)
|
||||
selection.addRange(range)
|
||||
document.execCommand('copy')
|
||||
selection.removeAllRanges()
|
||||
document.body.removeChild(node)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {value = '', ...rest} = this.props
|
||||
return (
|
||||
<Button onClick={() => this.copy()} type="button" {...rest}>
|
||||
<Octicon icon={Clippy} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import React from 'react'
|
||||
import HTMLtoJSX from 'html-2-jsx'
|
||||
import styled from 'styled-components'
|
||||
import {Absolute, BorderBox, Box, StyledOcticon as Octicon, Relative, Text} from '@primer/components'
|
||||
import {LiveEditor, LiveError, LivePreview, LiveProvider} from 'react-live'
|
||||
import {getIconByName} from '@githubprimer/octicons-react'
|
||||
import ClipboardCopy from './ClipboardCopy'
|
||||
import Frame from './Frame'
|
||||
import CodeExampleStyles from './CodeExampleStyles'
|
||||
|
||||
const StyledLiveProvider = styled(LiveProvider)`
|
||||
${CodeExampleStyles}
|
||||
`
|
||||
|
||||
const LANG_PATTERN = /\blanguage-\.?(jsx|html)\b/
|
||||
|
||||
const converter = new HTMLtoJSX({
|
||||
indent: ' ',
|
||||
createClass: false
|
||||
})
|
||||
|
||||
const defaultTransform = code => `<React.Fragment>${code}</React.Fragment>`
|
||||
|
||||
const languageTransforms = {
|
||||
html: html => defaultTransform(converter.convert(html)),
|
||||
jsx: defaultTransform
|
||||
}
|
||||
|
||||
export default function CodeExample(props) {
|
||||
const {children, dangerouslySetInnerHTML, inert, source, ...rest} = props
|
||||
const lang = getLanguage(props.className)
|
||||
if (lang && !inert) {
|
||||
const liveProps = {
|
||||
code: source,
|
||||
scope: {Octicon, getIconByName},
|
||||
transformCode: getTransformForLanguage(lang),
|
||||
mountStylesheet: false
|
||||
}
|
||||
return (
|
||||
<StyledLiveProvider {...liveProps}>
|
||||
<BorderBox {...rest}>
|
||||
<BorderBox bg="white" border={0} borderBottom={1} borderRadius={0}>
|
||||
<Frame>
|
||||
<LivePreview />
|
||||
</Frame>
|
||||
</BorderBox>
|
||||
<Box as={Relative} bg="gray.1" p={3}>
|
||||
<LiveEditor style={{margin: 0, padding: 0}} />
|
||||
<Absolute right={0} top={0} m={3}>
|
||||
<ClipboardCopy value={source} />
|
||||
</Absolute>
|
||||
<Text
|
||||
as={LiveError}
|
||||
fontFamily="mono"
|
||||
style={{
|
||||
overflow: 'auto',
|
||||
whiteSpace: 'pre'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</BorderBox>
|
||||
</StyledLiveProvider>
|
||||
)
|
||||
} else {
|
||||
const rest = {
|
||||
children,
|
||||
dangerouslySetInnerHTML
|
||||
}
|
||||
// eslint-disable-next-line react/no-danger-with-children
|
||||
return <BorderBox data-source={source} as="pre" {...rest} />
|
||||
}
|
||||
}
|
||||
|
||||
CodeExample.defaultProps = {
|
||||
my: 4
|
||||
}
|
||||
|
||||
function getLanguage(className) {
|
||||
const match = className && className.match(LANG_PATTERN)
|
||||
return match ? match[1] : undefined
|
||||
}
|
||||
|
||||
function getTransformForLanguage(lang) {
|
||||
return lang in languageTransforms ? languageTransforms[lang] : null
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
const styles = `
|
||||
code,
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
tab-size: 4;
|
||||
hyphens: none;
|
||||
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
line-height: 1.4;
|
||||
direction: ltr;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
pre[class*='language-'] {
|
||||
overflow: auto;
|
||||
margin: 1em 0;
|
||||
padding: 1.2em;
|
||||
border-radius: 3px;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
p code,
|
||||
li code,
|
||||
table code {
|
||||
margin: 0;
|
||||
border-radius: 3px;
|
||||
padding: 0.2em 0;
|
||||
font-size: 85%;
|
||||
}
|
||||
p code:before, p code:after,
|
||||
li code:before,
|
||||
li code:after,
|
||||
table code:before,
|
||||
table code:after {
|
||||
letter-spacing: -0.2em;
|
||||
content: "00a0";
|
||||
}
|
||||
|
||||
code,
|
||||
:not(pre) > code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*='language-'] {
|
||||
padding: 0.1em;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
.token.comment, .token.prolog, .token.doctype, .token.cdata {
|
||||
color: #969896;
|
||||
}
|
||||
.token.punctuation, .token.string, .token.atrule, .token.attr-value {
|
||||
color: #183691;
|
||||
}
|
||||
.token.property, .token.tag {
|
||||
color: #63a35c;
|
||||
}
|
||||
.token.boolean, .token.number {
|
||||
color: #0086b3;
|
||||
}
|
||||
.token.selector, .token.attr-name, .token.attr-value .punctuation:first-child, .token.keyword, .token.regex, .token.important {
|
||||
color: #a71d5d;
|
||||
}
|
||||
.token.operator, .token.entity, .token.url, .language-css .token.string {
|
||||
color: #a71d5d;
|
||||
}
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: 0.7;
|
||||
}
|
||||
`
|
||||
|
||||
export default styles
|
@ -1,101 +0,0 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {Link, Text, Avatar, Flex} from '@primer/components'
|
||||
|
||||
function generateContributors(authors) {
|
||||
const logins = []
|
||||
const uniqueAuthors = authors.filter(author => {
|
||||
if (logins.includes(author.login)) {
|
||||
return false
|
||||
} else {
|
||||
logins.push(author.login)
|
||||
return true
|
||||
}
|
||||
})
|
||||
return uniqueAuthors.map((author, i) => (
|
||||
<>
|
||||
<Link href={`https://github.com/${author.login}`}>{author.login}</Link>
|
||||
{authors.length > 1 && authors.length - 1 !== i && ', '}
|
||||
</>
|
||||
))
|
||||
}
|
||||
|
||||
function generateLastEdited(lastAuthor) {
|
||||
if (lastAuthor) {
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
]
|
||||
const day = lastAuthor.time.getDate()
|
||||
const month = months[lastAuthor.time.getMonth()]
|
||||
const year = lastAuthor.time.getFullYear()
|
||||
return (
|
||||
<Flex alignItems="center">
|
||||
<Text fontWeight="bold" lineHeight={2} mr={1}>
|
||||
Last edited by:{' '}
|
||||
</Text>
|
||||
<Avatar src={lastAuthor.avatar} mr={1} />
|
||||
<Text>
|
||||
<Link href={`https://github.com/${lastAuthor.login}`}> {lastAuthor.login}</Link> on{' '}
|
||||
<Link color="gray.5" href={lastAuthor.commitUrl}>{`${month} ${day}, ${year}`}</Link>
|
||||
</Text>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Contributors = ({filePath, repoPath, contributors}) => {
|
||||
const [authors, setAuthors] = useState([])
|
||||
useEffect(() => {
|
||||
const url = `https://api.github.com/repos/${repoPath}/commits?path=${filePath}`
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(commits => {
|
||||
const commitData = []
|
||||
const ids = []
|
||||
for (let i = 0; i < commits.length; i++) {
|
||||
if (!ids.includes(commits[i].author.id)) {
|
||||
commitData.push({
|
||||
login: commits[i].author.login,
|
||||
avatar: commits[i].author.avatar_url,
|
||||
time: new Date(commits[i].commit.author.date),
|
||||
commitUrl: commits[i].html_url
|
||||
})
|
||||
ids.push(commits[i].author.id)
|
||||
}
|
||||
}
|
||||
setAuthors(commitData)
|
||||
})
|
||||
}, [filePath])
|
||||
|
||||
return (
|
||||
<Text fontSize={1}>
|
||||
<Text fontWeight="bold" lineHeight={2}>
|
||||
Contributors:{' '}
|
||||
</Text>
|
||||
{generateContributors([...contributors, ...authors])}
|
||||
{generateLastEdited(authors[0])}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
Contributors.defaultProps = {
|
||||
contributors: []
|
||||
}
|
||||
|
||||
Contributors.propTypes = {
|
||||
contributors: PropTypes.object.isRequired,
|
||||
filePath: PropTypes.string.isRequired,
|
||||
repoPath: PropTypes.string.isRequired
|
||||
}
|
||||
export default Contributors
|
@ -1,69 +0,0 @@
|
||||
import React, {useState} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import {theme} from '@primer/components'
|
||||
import {space, color} from 'styled-system'
|
||||
|
||||
const DetailsReset = styled('details')`
|
||||
cursor: pointer;
|
||||
& > summary {
|
||||
list-style: none;
|
||||
}
|
||||
& > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
function getRenderer(children) {
|
||||
return typeof children === 'function' ? children : () => children
|
||||
}
|
||||
|
||||
function DetailsBase({children, overlay, render = getRenderer(children), ...rest}) {
|
||||
const [open, setOpen] = useState(Boolean(rest.open))
|
||||
|
||||
function toggle(event) {
|
||||
if (event) event.preventDefault()
|
||||
if (overlay) {
|
||||
openMenu()
|
||||
} else {
|
||||
setOpen(!open)
|
||||
}
|
||||
}
|
||||
|
||||
function openMenu() {
|
||||
if (!open) {
|
||||
setOpen(true)
|
||||
document.addEventListener('click', closeMenu)
|
||||
}
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
setOpen(false)
|
||||
document.removeEventListener('click', closeMenu)
|
||||
}
|
||||
return (
|
||||
<DetailsReset {...rest} open={open} overlay={overlay}>
|
||||
{render({open, toggle})}
|
||||
</DetailsReset>
|
||||
)
|
||||
}
|
||||
|
||||
const Details = styled(DetailsBase)`
|
||||
${space};
|
||||
${color};
|
||||
`
|
||||
|
||||
Details.defaultProps = {
|
||||
theme,
|
||||
overlay: false
|
||||
}
|
||||
|
||||
Details.propTypes = {
|
||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
||||
className: PropTypes.string,
|
||||
open: PropTypes.bool,
|
||||
overlay: PropTypes.bool,
|
||||
render: PropTypes.func,
|
||||
theme: PropTypes.object
|
||||
}
|
||||
|
||||
export default Details
|
@ -1,66 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import Measure from 'react-measure'
|
||||
import {BorderBox} from '@primer/components'
|
||||
import {assetPrefix} from './utils'
|
||||
|
||||
export default class Frame extends React.Component {
|
||||
static defaultProps = {
|
||||
border: 0,
|
||||
borderRadius: 0,
|
||||
minHeight: 0,
|
||||
width: '100%'
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {files: [], height: props.height}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.doc = this.iframe.contentDocument
|
||||
const files = JSON.parse(document.body.dataset.files || '[]')
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
this.setState({loaded: false, files})
|
||||
this.iframe.addEventListener('load', () => {
|
||||
this.setState({loaded: true})
|
||||
})
|
||||
}
|
||||
|
||||
getHead() {
|
||||
const {files} = this.state
|
||||
return files
|
||||
? files
|
||||
.filter(file => file.endsWith('.css'))
|
||||
.map(file => <link rel="stylesheet" href={`${assetPrefix}/_next/${file}`} key={file} />)
|
||||
: null
|
||||
}
|
||||
|
||||
getBody(children) {
|
||||
return (
|
||||
<Measure bounds onResize={rect => this.setHeight(rect.bounds.height)}>
|
||||
{({measureRef}) => <div ref={measureRef}>{children}</div>}
|
||||
</Measure>
|
||||
)
|
||||
}
|
||||
|
||||
setHeight(height) {
|
||||
// console.warn('height:', height)
|
||||
this.setState({height})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {children, ...rest} = this.props
|
||||
const {height = 'auto'} = this.state
|
||||
return (
|
||||
<BorderBox as="iframe" style={{height}} {...rest} ref={node => (this.iframe = node)}>
|
||||
{this.doc
|
||||
? [
|
||||
ReactDOM.createPortal(this.getHead(), this.doc.head),
|
||||
ReactDOM.createPortal(this.getBody(children), this.doc.body)
|
||||
]
|
||||
: null}
|
||||
</BorderBox>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import {MarkGithub, ChevronRight} from '@githubprimer/octicons-react'
|
||||
import {Text, Flex, Sticky, BorderBox, Box, StyledOcticon} from '@primer/components'
|
||||
import Link from './Link'
|
||||
import Search from './Search'
|
||||
|
||||
const BoxShadow = styled(Box)`
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
`
|
||||
|
||||
const HeaderText = props => <Text fontFamily="mono" fontSize={2} color="blue.4" {...props} />
|
||||
|
||||
const Header = ({title, subtitle, root, subfolder, documents, children}) => (
|
||||
<Sticky zIndex={100}>
|
||||
<BoxShadow py={3} bg="black" color="white">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Flex alignItems="center">
|
||||
<Link href={root} color="white" ml={5}>
|
||||
<StyledOcticon color="blue.4" icon={MarkGithub} size="medium" />
|
||||
</Link>
|
||||
<Box display={['none', 'inline-block', 'inline-block', 'inline-block']}>
|
||||
<Link href={root} mr={2} ml={3}>
|
||||
<HeaderText>{title}</HeaderText>
|
||||
</Link>
|
||||
{subfolder && <StyledOcticon icon={ChevronRight} mx={1} color="blue.4" />}
|
||||
</Box>
|
||||
{subfolder && (
|
||||
<Link href={`${root}/${subfolder}`} ml={2} mr={4}>
|
||||
<HeaderText>{subtitle}</HeaderText>
|
||||
</Link>
|
||||
)}
|
||||
{subfolder && (
|
||||
<Box display={['none', 'none', 'none', 'flex']}>
|
||||
<Search documents={documents} subfolder={subfolder} />
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
<Box display={['none', 'none', 'none', 'flex']}>{children}</Box>
|
||||
<Box display={['flex', 'flex', 'flex', 'none']}>
|
||||
<Link href="#jumpnav">
|
||||
<BorderBox
|
||||
border={1}
|
||||
borderColor="gray.6"
|
||||
borderRadius={3}
|
||||
color="white"
|
||||
display="inline-block"
|
||||
px="12px"
|
||||
py="6px"
|
||||
mr={3}
|
||||
>
|
||||
<Text fontWeight="bold" color="blue.2" fontSize={1}>
|
||||
Menu
|
||||
</Text>
|
||||
</BorderBox>
|
||||
</Link>
|
||||
{subfolder && <Search documents={documents} root={root} />}
|
||||
</Box>
|
||||
</Flex>
|
||||
</BoxShadow>
|
||||
</Sticky>
|
||||
)
|
||||
|
||||
Header.propTypes = {
|
||||
root: PropTypes.string.isRequired,
|
||||
subfolder: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default Header
|
@ -1,28 +0,0 @@
|
||||
import React from 'react'
|
||||
import {Link} from '@primer/components'
|
||||
import NavDropdown from './NavDropdown'
|
||||
import NavItem from './NavItem'
|
||||
|
||||
const JumpNav = () => {
|
||||
return (
|
||||
<>
|
||||
<Link color="blue.2" mx={3} href="https://primer.style/news">
|
||||
What’s New
|
||||
</Link>
|
||||
<NavDropdown title="Design">
|
||||
<NavItem href="https://primer.style/design">Interface Guidelines</NavItem>
|
||||
<NavItem href="https://octicons.github.com/">Octicons</NavItem>
|
||||
<NavItem href="https://primer.style/css/tools/prototyping">Prototyping</NavItem>
|
||||
</NavDropdown>
|
||||
<NavDropdown title="Development" direction="sw">
|
||||
<NavItem href="https://primer.style/css">Primer CSS</NavItem>
|
||||
<NavItem href="https://primer.style/components">Primer Components</NavItem>
|
||||
</NavDropdown>
|
||||
<Link color="blue.2" mr={3} href="https://primer.style/team">
|
||||
Team
|
||||
</Link>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default JumpNav
|
11
docs/Link.js
11
docs/Link.js
@ -1,11 +0,0 @@
|
||||
import React from 'react'
|
||||
import NextLink from 'next/link'
|
||||
import {Link as PrimerLink} from '@primer/components'
|
||||
|
||||
export default function Link({href, ...rest}) {
|
||||
return (
|
||||
<NextLink href={href}>
|
||||
<PrimerLink href={href} {...rest} />
|
||||
</NextLink>
|
||||
)
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {StyledOcticon, Heading, Box, Text} from '@primer/components'
|
||||
import {Link} from '@githubprimer/octicons-react'
|
||||
import slugify from 'slugify'
|
||||
|
||||
const Anchor = ({id}) => (
|
||||
<a href={`#${id}`}>
|
||||
<StyledOcticon color="black" icon={Link} />
|
||||
</a>
|
||||
)
|
||||
|
||||
const StyledHeading = styled(Heading)`
|
||||
&:hover .anchorWrapper,
|
||||
&:focus .anchorWrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
`
|
||||
const MarkdownHeading = ({children, className, ...rest}) => {
|
||||
const id = slugify(children.toString())
|
||||
return (
|
||||
<StyledHeading id={id} className={className} {...rest}>
|
||||
<Box as={Text} lineHeight={1} className="anchorWrapper" pr={1} ml={'-20px'} display={'none'}>
|
||||
<Anchor id={id} />
|
||||
</Box>
|
||||
{children}
|
||||
</StyledHeading>
|
||||
)
|
||||
}
|
||||
|
||||
export default MarkdownHeading
|
@ -1,69 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import {ChevronDown} from '@githubprimer/octicons-react'
|
||||
import {Text, Flex, Relative, StyledOcticon, Absolute, Box} from '@primer/components'
|
||||
import getDirectionStyles from './getDirectionStyles'
|
||||
import Details from './Details'
|
||||
|
||||
const DropdownMenu = styled.div`
|
||||
box-shadow: 0px 4px 12px rgba(27, 31, 35, 0.15);
|
||||
border: 1px solid rgba(219, 237, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
max-width: 215px;
|
||||
overflow: hidden;
|
||||
${props => (props.direction ? getDirectionStyles(props.theme, props.direction) : '')};
|
||||
`
|
||||
function NavDropdown({children, title, color, direction = 'se', ...rest}) {
|
||||
return (
|
||||
<Box {...rest}>
|
||||
<Details
|
||||
overlay
|
||||
mx={3}
|
||||
render={({toggle}) => (
|
||||
<>
|
||||
<Text color={color} as="summary" onClick={toggle}>
|
||||
{title} <StyledOcticon icon={ChevronDown} />
|
||||
</Text>
|
||||
<Relative>
|
||||
<DropdownMenu as={Absolute} py={2} zIndex={90} bg="black" direction={direction}>
|
||||
{children}
|
||||
</DropdownMenu>
|
||||
</Relative>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const Responsive = ({children, title, color}) => {
|
||||
return (
|
||||
<Details
|
||||
render={({toggle}) => (
|
||||
<>
|
||||
<Flex as="summary" alignItems="center" justifyContent="space-between" onClick={toggle}>
|
||||
<Text fontWeight="bold" color={color}>
|
||||
{title}
|
||||
</Text>
|
||||
<StyledOcticon icon={ChevronDown} />
|
||||
</Flex>
|
||||
|
||||
<Relative>{children}</Relative>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
NavDropdown.Responsive = Responsive
|
||||
|
||||
NavDropdown.defaultProps = {
|
||||
color: 'blue.2'
|
||||
}
|
||||
|
||||
NavDropdown.propTypes = {
|
||||
color: PropTypes.string
|
||||
}
|
||||
|
||||
export default NavDropdown
|
@ -1,20 +0,0 @@
|
||||
import {theme, Link} from '@primer/components'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const NavItem = styled(Link)`
|
||||
color: ${theme.colors.blue[2]};
|
||||
display: block;
|
||||
padding: 0 ${theme.space[3]}px;
|
||||
line-height: 30px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 215px;
|
||||
&:hover {
|
||||
color: ${theme.colors.black};
|
||||
background-color: ${theme.colors.blue[4]};
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
export default NavItem
|
@ -1,14 +0,0 @@
|
||||
import React from 'react'
|
||||
import {withRouter} from 'next/router'
|
||||
import {Box} from '@primer/components'
|
||||
import PageLink from './PageLink'
|
||||
|
||||
const NavLink = withRouter(({href, router, ...rest}) => {
|
||||
return (
|
||||
<Box {...rest}>
|
||||
<PageLink href={href} color={router.pathname === href ? 'black' : undefined} fontSize={1} {...rest} />
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
|
||||
export default NavLink
|
@ -1,25 +0,0 @@
|
||||
import React from 'react'
|
||||
import Pages from '@primer/next-pages'
|
||||
import {nodeSort} from './utils'
|
||||
import NavLink from './NavLink'
|
||||
import SectionLink from './SectionLink'
|
||||
|
||||
const {pageMap = new Map()} = Pages
|
||||
|
||||
/**
|
||||
* A <NavList> renders a <Section.Link> for the given `path` and looks up the
|
||||
* path in the page tree. If a node is found, it renders a <NavLink> for each
|
||||
* of the node's children.
|
||||
*/
|
||||
export default function NavList({path}) {
|
||||
const node = pageMap.get(path)
|
||||
const children = node ? node.children.sort(nodeSort) : []
|
||||
return (
|
||||
<>
|
||||
<SectionLink color="gray.9" href={path} mb={3} />
|
||||
{children.map(child => (
|
||||
<NavLink mt={2} href={child.path} key={child.path} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import React from 'react'
|
||||
import Link from './Link'
|
||||
import {rootPage} from './utils'
|
||||
|
||||
/**
|
||||
* The NodeLink component takes an `href` and optional `children`.
|
||||
* If no `children` are provided, we look up the "node" of the corresponding
|
||||
* page in the tree (the one whose `path` matches the given `href`) and use
|
||||
* that node's `meta.title` if it's set. In other words, given the following
|
||||
* pages/foo/bar.md:
|
||||
*
|
||||
* ```md
|
||||
* ---
|
||||
* title: Foo Bar
|
||||
* ---
|
||||
* ```
|
||||
*
|
||||
* The following instance of NodeLink should render a link to "/foo/bar" with
|
||||
* "Foo Bar" as its text:
|
||||
*
|
||||
* ```jsx
|
||||
* <NodeLink href="/foo/bar" />
|
||||
* ```
|
||||
*/
|
||||
export default function NodeLink(props) {
|
||||
const {href, children: content} = props
|
||||
if (content) {
|
||||
return <Link {...props} />
|
||||
}
|
||||
const node = rootPage.first(node => node.path === href)
|
||||
const children = (node ? node.meta.title : null) || href
|
||||
return <Link {...props}>{children}</Link>
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
import React from 'react'
|
||||
import Link from './Link'
|
||||
import Pages from '@primer/next-pages'
|
||||
|
||||
const {pageMap = new Map()} = Pages
|
||||
|
||||
/**
|
||||
* The PageLink component takes an `href` and optional `children`.
|
||||
* If no `children` are provided, we look up the "node" of the corresponding
|
||||
* page in the tree (the one whose `path` matches the given `href`) and use
|
||||
* that page's `meta.title` if it's set. In other words, given the following
|
||||
* pages/foo/bar.md:
|
||||
*
|
||||
* ```md
|
||||
* ---
|
||||
* title: Foo Bar
|
||||
* ---
|
||||
* ```
|
||||
*
|
||||
* The following instance of PageLink should render a link to "/foo/bar" with
|
||||
* "Foo Bar" as its text:
|
||||
*
|
||||
* ```jsx
|
||||
* <PageLink href="/foo/bar" />
|
||||
* ```
|
||||
*/
|
||||
export default function PageLink(props) {
|
||||
const {href, children: content} = props
|
||||
if (content) {
|
||||
return <Link {...props} />
|
||||
}
|
||||
const page = pageMap.get(href)
|
||||
if (!page) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`no page for "${href}"`, pageMap.keys())
|
||||
}
|
||||
const children = (page ? page.meta.title : null) || href
|
||||
return <Link {...props}>{children}</Link>
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import React from 'react'
|
||||
import {Link as PrimerLink, BorderBox, Box} from '@primer/components'
|
||||
import NavDropdown from './NavDropdown'
|
||||
import Link from './Link'
|
||||
|
||||
const DropdownLink = ({href, children}) => (
|
||||
<Box mt={2}>
|
||||
<Link fontSize={1} href={href}>
|
||||
{children}
|
||||
</Link>
|
||||
</Box>
|
||||
)
|
||||
|
||||
const ResponsiveJumpNav = () => {
|
||||
return (
|
||||
<BorderBox p={5} bg="gray.1" border={0} borderBottom={1} borderColor="gray.2" borderRadius={0} width="100%">
|
||||
<BorderBox py={3} borderColor="gray.2" borderTop={0} borderLeft={0} borderRight={0} borderBottom={1}>
|
||||
<PrimerLink color="black" fontWeight="bold" my={2} href="https://primer.style/news">
|
||||
What’s New
|
||||
</PrimerLink>
|
||||
</BorderBox>
|
||||
<BorderBox py={3} borderColor="gray.2" borderTop={0} borderLeft={0} borderRight={0} borderBottom={1}>
|
||||
<PrimerLink color="black" fontWeight="bold" my={2} href="https://primer.style/team">
|
||||
Team
|
||||
</PrimerLink>
|
||||
</BorderBox>
|
||||
<BorderBox py={3} borderColor="gray.2" borderTop={0} borderLeft={0} borderRight={0} borderBottom={1}>
|
||||
<NavDropdown.Responsive color="black" title="Design">
|
||||
<DropdownLink href="https://primer.style/design">Interface Guidelines</DropdownLink>
|
||||
<DropdownLink href="https://octicons.github.com/">Icons</DropdownLink>
|
||||
<DropdownLink href="https://github.com/primer/presentations">Presentations</DropdownLink>
|
||||
</NavDropdown.Responsive>
|
||||
</BorderBox>
|
||||
<Box pt={3}>
|
||||
<NavDropdown.Responsive color="black" title="Development">
|
||||
<DropdownLink href="https://primer.style/css">Primer CSS</DropdownLink>
|
||||
<DropdownLink href="https://primer.style/components">Primer Components</DropdownLink>
|
||||
<DropdownLink href="https://github.com/primer/deploy">Deploy</DropdownLink>
|
||||
<DropdownLink href="https://primer.style/css/tools/prototyping">Prototyping</DropdownLink>
|
||||
</NavDropdown.Responsive>
|
||||
</Box>
|
||||
</BorderBox>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResponsiveJumpNav
|
@ -1,64 +0,0 @@
|
||||
import {StyledOcticon} from '@primer/components'
|
||||
import {Search, X} from '@githubprimer/octicons-react'
|
||||
import React, {useState} from 'react'
|
||||
import styled from 'styled-components'
|
||||
import SearchInput from './SearchInput'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
position: relative;
|
||||
${props =>
|
||||
props.open &&
|
||||
`
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background: #1b1f23;
|
||||
padding: 16px 0 16px 16px;
|
||||
`};
|
||||
`
|
||||
|
||||
const StyledButton = styled.button`
|
||||
background: none;
|
||||
display: inline-block;
|
||||
color: #c8e1ff;
|
||||
margin-right: 16px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
border: 1px solid;
|
||||
border-color: #586069;
|
||||
border-radius: 3px;
|
||||
height: 38px;
|
||||
right: 0px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: #c8e1ff;
|
||||
background-color: #0366d6;
|
||||
background-image: none;
|
||||
border-color: #0366d6;
|
||||
}
|
||||
`
|
||||
|
||||
const ResponsiveSearchInput = props => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const handleClick = () => {
|
||||
if (open) {
|
||||
props.closeMenu()
|
||||
}
|
||||
setOpen(!open)
|
||||
}
|
||||
return (
|
||||
<Wrapper open={open}>
|
||||
{open && <SearchInput mr={3} width="100%" {...props} />}
|
||||
<StyledButton onClick={handleClick}>
|
||||
<StyledOcticon icon={open ? X : Search} />
|
||||
</StyledButton>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResponsiveSearchInput
|
@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
import {addPath} from './utils'
|
||||
/**
|
||||
* <RouteMatch> is just a way to conditionally render content without a wrapper
|
||||
* element when contained directly in a <Router>:
|
||||
*
|
||||
* ```jsx
|
||||
* <Router>
|
||||
* <RouteMatch path="/some/dir">
|
||||
* this will only show up on pages whose path begins with "/some/dir"
|
||||
* </RouteMatch>
|
||||
* </Router>
|
||||
* ```
|
||||
*/
|
||||
export default function RouteMatch({path, children}) {
|
||||
return path ? React.Children.map(children, child => addPath(child, path)) : children
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import React from 'react'
|
||||
import {withRouter} from 'next/router'
|
||||
|
||||
/**
|
||||
* This is inspired by React Router's <Router> component, in that it looks for
|
||||
* children with the `path` prop and only renders the _first_ one that matches
|
||||
* the beginning of the current path. Children without a `path` prop are always
|
||||
* rendered.
|
||||
*/
|
||||
const Router = withRouter(({router, children}) => {
|
||||
let matched = false
|
||||
return React.Children.toArray(children).map(child => {
|
||||
if (child.props.path) {
|
||||
if (!matched && router.pathname.indexOf(child.props.path) === 0) {
|
||||
return (matched = child)
|
||||
}
|
||||
} else {
|
||||
return child
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export default Router
|
136
docs/Search.js
136
docs/Search.js
@ -1,136 +0,0 @@
|
||||
// downshift applies htmlFor for the label, but eslint doesn't recognize that
|
||||
// also disabling the specific line causes some weirdness when running lint for syntax
|
||||
/* eslint-disable jsx-a11y/label-has-for */
|
||||
import React, {useState} from 'react'
|
||||
import Router from 'next/router'
|
||||
import lunr from 'lunr'
|
||||
import {Relative, Box, Text} from '@primer/components'
|
||||
import SearchItem from './SearchItem'
|
||||
import Downshift from 'downshift'
|
||||
import SearchInput from './SearchInput'
|
||||
import SearchResults from './SearchResults'
|
||||
import ResponsiveSearchInput from './ResponsiveSearchInput'
|
||||
|
||||
const generateBreadcrumb = path => {
|
||||
const a = path
|
||||
.toLowerCase()
|
||||
.split('-')
|
||||
.join(' ')
|
||||
const b = a.split('/').join(' / ')
|
||||
return b
|
||||
.split(' ')
|
||||
.map(s => s.charAt(0).toUpperCase() + s.substring(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
function Search({subfolder, documents}) {
|
||||
const idx = lunr(function() {
|
||||
this.ref('path') //eslint-disable-line
|
||||
this.field('title')//eslint-disable-line
|
||||
this.field('content')//eslint-disable-line
|
||||
this.field('keywords')//eslint-disable-line
|
||||
for (const doc of Object.values(documents)) {
|
||||
this.add(doc) //eslint-disable-line
|
||||
}
|
||||
})
|
||||
|
||||
const [results, setResults] = useState([])
|
||||
|
||||
const onChange = e => {
|
||||
if (e.target) {
|
||||
setResults(idx.search(`${e.target.value}*`))
|
||||
}
|
||||
}
|
||||
|
||||
const renderResults = (selectedItem, getItemProps, highlightedIndex) => {
|
||||
if (results.length < 1) {
|
||||
return (
|
||||
<Text as={Relative} p={2} color="black">
|
||||
No results found
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
return results.map((result, index) => {
|
||||
const doc = documents[result.ref]
|
||||
if (!doc.path) return
|
||||
return (
|
||||
<SearchItem //eslint-disable-line
|
||||
{...getItemProps({
|
||||
item: result,
|
||||
index,
|
||||
key: result.ref,
|
||||
href: `/${subfolder}/${doc.path}`,
|
||||
isHighlighted: highlightedIndex === index
|
||||
})}
|
||||
>
|
||||
{doc.path && (
|
||||
<Text as="div" fontSize={0}>
|
||||
{generateBreadcrumb(doc.path)}
|
||||
</Text>
|
||||
)}
|
||||
{doc.title}
|
||||
</SearchItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function stateReducer(state, changes) {
|
||||
switch (changes.type) {
|
||||
case Downshift.stateChangeTypes.keyDownEnter:
|
||||
case Downshift.stateChangeTypes.clickItem:
|
||||
return {
|
||||
...changes,
|
||||
inputValue: ''
|
||||
}
|
||||
default:
|
||||
return changes
|
||||
}
|
||||
}
|
||||
|
||||
const onSelect = item => {
|
||||
Router.push(`/${subfolder}/${item.ref}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Downshift
|
||||
onChange={onChange}
|
||||
itemToString={item => (item ? documents[item.ref].title : '')}
|
||||
stateReducer={stateReducer}
|
||||
onSelect={onSelect}
|
||||
>
|
||||
{({
|
||||
getInputProps,
|
||||
getMenuProps,
|
||||
getLabelProps,
|
||||
closeMenu,
|
||||
getItemProps,
|
||||
isOpen,
|
||||
highlightedIndex,
|
||||
selectedItem
|
||||
}) => (
|
||||
<div>
|
||||
<label hidden {...getLabelProps()}>
|
||||
{' '}
|
||||
Search docs
|
||||
</label>
|
||||
<Box display={['none', 'none', 'none', 'flex']}>
|
||||
<SearchInput placeholder="Search" {...getInputProps({onChange})} />
|
||||
</Box>
|
||||
<Box
|
||||
display={['inline-block', 'inline-block', 'inline-block', 'none']}
|
||||
width={['100%', '100%', '100%', 'initial']}
|
||||
>
|
||||
<ResponsiveSearchInput closeMenu={closeMenu} {...getInputProps({onChange})} />
|
||||
</Box>
|
||||
<SearchResults color="black" open={isOpen} {...getMenuProps()}>
|
||||
{renderResults(selectedItem, getItemProps, highlightedIndex)}
|
||||
</SearchResults>
|
||||
</div>
|
||||
)}
|
||||
</Downshift>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Search
|
@ -1,19 +0,0 @@
|
||||
import {TextInput} from '@primer/components'
|
||||
import {space, width} from 'styled-system'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const SearchInput = styled(TextInput)`
|
||||
color: #fff;
|
||||
background-color: rgba(255, 255, 255, 0.07);
|
||||
border: 1px;
|
||||
border: 1px solid transparent;
|
||||
&:focus,
|
||||
&:active {
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
${space};
|
||||
${width};
|
||||
`
|
||||
export default SearchInput
|
@ -1,15 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
const SearchItem = styled.a`
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: ${props => (props.isHighlighted ? '#fff' : '#24292e')};
|
||||
background-color: ${props => (props.isHighlighted ? '#0366d6' : '#fff')};
|
||||
padding: 8px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
export default SearchItem
|
@ -1,25 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
const SearchResults = styled.div`
|
||||
box-shadow: 0 1px 5px rgba(27, 31, 35, 0.15);
|
||||
border: 1px solid #e1e4e8;
|
||||
min-width: 320px;
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
max-height: 90vh;
|
||||
overflow-y: scroll;
|
||||
background-color: #fff;
|
||||
display: ${props => (props.open ? 'block' : 'none')};
|
||||
position: absolute;
|
||||
|
||||
@media (max-width: 1012px) {
|
||||
width: 100%;
|
||||
max-height: inherit;
|
||||
height: 100vh;
|
||||
margin-top: 30px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
`
|
||||
|
||||
export default SearchResults
|
@ -1,28 +0,0 @@
|
||||
import React from 'react'
|
||||
import {BorderBox} from '@primer/components'
|
||||
import NavList from './NavList'
|
||||
import {addPath} from './utils'
|
||||
|
||||
/**
|
||||
* A <Section> gets a `path` and optional children. If it has children it will
|
||||
* render those and prepend each child's `href` prop with the provided `path`.
|
||||
* This means that you can do:
|
||||
*
|
||||
* ```jsx
|
||||
* <Section path="/section">
|
||||
* <Link href="foo">Links to /section/foo</Link>
|
||||
* </Section>
|
||||
* ```
|
||||
*
|
||||
* If no children are provided, it renders a <NavList> with the provided
|
||||
* `path`.
|
||||
*/
|
||||
const Section = ({path, children}) => {
|
||||
return (
|
||||
<BorderBox p={5} border={0} borderBottom={1} borderRadius={0} width="100%">
|
||||
{children && path ? React.Children.map(children, child => addPath(child, path)) : <NavList path={path} />}
|
||||
</BorderBox>
|
||||
)
|
||||
}
|
||||
|
||||
export default Section
|
@ -1,12 +0,0 @@
|
||||
import React from 'react'
|
||||
import {withRouter} from 'next/router'
|
||||
import {Box} from '@primer/components'
|
||||
import PageLink from './PageLink'
|
||||
|
||||
const SectionLink = withRouter(({href, router, ...rest}) => (
|
||||
<Box {...rest}>
|
||||
<PageLink href={href} {...rest} fontSize={2} fontWeight={router.pathname.startsWith(href) ? 'bold' : null} />
|
||||
</Box>
|
||||
))
|
||||
|
||||
export default SectionLink
|
@ -1,19 +0,0 @@
|
||||
import React from 'react'
|
||||
import {Box, Flex, Relative} from '@primer/components'
|
||||
import Router from './Router'
|
||||
|
||||
function SideNav(props) {
|
||||
return (
|
||||
<Box height="100vh">
|
||||
<Relative as="nav">
|
||||
<Box {...props}>
|
||||
<Flex flexDirection="column" alignItems="start">
|
||||
<Router>{props.children}</Router>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Relative>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default SideNav
|
@ -1,28 +1,2 @@
|
||||
export {default as ClipboardCopy} from './ClipboardCopy'
|
||||
export {default as CodeExample} from './CodeExample'
|
||||
export {default as Contributors} from './Contributors'
|
||||
export {default as Details} from './Details'
|
||||
export {default as Frame} from './Frame'
|
||||
export {default as Header} from './Header'
|
||||
export {default as Link} from './Link'
|
||||
export {default as MarkdownHeading} from './MarkdownHeading'
|
||||
export {default as NavDropdown} from './NavDropdown'
|
||||
export {default as NavItem} from './NavItem'
|
||||
export {default as NavLink} from './NavLink'
|
||||
export {default as NavList} from './NavList'
|
||||
export {default as Outline} from './Outline'
|
||||
export {default as PackageHeader} from './PackageHeader'
|
||||
export {default as PageLink} from './PageLink'
|
||||
export {default as JumpNav} from './JumpNav'
|
||||
export {default as ResponsiveJumpNav} from './ResponsiveJumpNav'
|
||||
export {default as RouteMatch} from './RouteMatch'
|
||||
export {default as Router} from './Router'
|
||||
export {default as Section} from './Section'
|
||||
export {default as Search} from './Search'
|
||||
export {default as SearchInput} from './SearchInput'
|
||||
export {default as ResponsiveSearchInput} from './ResponsiveSearchInput'
|
||||
export {default as SearchItem} from './SearchItem'
|
||||
export {default as SearchResults} from './SearchResults'
|
||||
export {default as SectionLink} from './SectionLink'
|
||||
export {default as SideNav} from './SideNav'
|
||||
export {default as StatusLabel} from './StatusLabel'
|
||||
|
@ -1,127 +0,0 @@
|
||||
import {get} from './constants'
|
||||
|
||||
const getDirectionStyles = (theme, direction) => {
|
||||
const map = {
|
||||
w: `
|
||||
top: 0;
|
||||
right: 100%;
|
||||
left: auto;
|
||||
width: auto;
|
||||
margin-top: 0;
|
||||
margin-right: 10px;
|
||||
|
||||
&::before {
|
||||
top: 10px;
|
||||
right: -16px;
|
||||
left: auto;
|
||||
border-color: transparent;
|
||||
border-left-color: ${get('colors.blackfade15')(theme)};
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: 11px;
|
||||
right: -14px;
|
||||
left: auto;
|
||||
border-color: transparent;
|
||||
border-left-color: ${get('colors.white')(theme)};
|
||||
}
|
||||
`,
|
||||
e: `
|
||||
top: 0;
|
||||
left: 100%;
|
||||
width: auto;
|
||||
margin-top: 0;
|
||||
margin-left: 10px;
|
||||
|
||||
&::before {
|
||||
top: 10px;
|
||||
left: -16px;
|
||||
border-color: transparent;
|
||||
border-right-color: ${get('colors.blackfade15')(theme)};
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: 11px;
|
||||
left: -14px;
|
||||
border-color: transparent;
|
||||
border-right-color: ${get('colors.white')(theme)};
|
||||
}
|
||||
`,
|
||||
ne: `
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
left: 0;
|
||||
margin-bottom: 3px;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
top: auto;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
&::before {
|
||||
bottom: -8px;
|
||||
left: 9px;
|
||||
border-top: 8px solid ${get('colors.blackfade15')(theme)};
|
||||
border-bottom: 0;
|
||||
border-left: 8px solid transparent;
|
||||
}
|
||||
|
||||
&::after {
|
||||
bottom: -7px;
|
||||
left: 10px;
|
||||
border-top: 7px solid ${get('colors.white')(theme)};
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 0;
|
||||
border-left: 7px solid transparent;
|
||||
}
|
||||
`,
|
||||
s: `
|
||||
right: 50%;
|
||||
left: auto;
|
||||
transform: translateX(50%);
|
||||
|
||||
&::before {
|
||||
top: -16px;
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: -14px;
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
}
|
||||
`,
|
||||
sw: `
|
||||
right: 0;
|
||||
left: auto;
|
||||
|
||||
&::before {
|
||||
top: -16px;
|
||||
right: 9px;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: -14px;
|
||||
right: 10px;
|
||||
left: auto;
|
||||
}
|
||||
`,
|
||||
se: `
|
||||
&::before {
|
||||
top: -16px;
|
||||
left: 9px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: -14px;
|
||||
left: 10px;
|
||||
}
|
||||
`
|
||||
}
|
||||
return map[direction]
|
||||
}
|
||||
|
||||
export default getDirectionStyles
|
@ -1,43 +0,0 @@
|
||||
import Router from 'next/router'
|
||||
|
||||
/**
|
||||
* Export this as your default from a page, and it'll redirect both server-
|
||||
* and client-side:
|
||||
*
|
||||
* ```js
|
||||
* import {redirect} from '../src/utils'
|
||||
* export default redirect('/some/path')
|
||||
* ```
|
||||
*/
|
||||
export default function redirect(uri, status = 303) {
|
||||
// XXX this doesn't need to extend React.Component because
|
||||
// it doesn't "do" anything React-y
|
||||
return class {
|
||||
static getInitialProps({res}) {
|
||||
// the "context" object passed to getInitialProps() will
|
||||
// have a "res" (response) object if we're server-side
|
||||
if (res) {
|
||||
res.writeHead(status, {Location: uri})
|
||||
res.end()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
Router.replace(uri)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function redirectTrailingSlash(context, status = 301) {
|
||||
const {
|
||||
req: {url},
|
||||
res
|
||||
} = context
|
||||
if (url.endsWith('/')) {
|
||||
const withoutSlash = url.substr(0, url.length - 1)
|
||||
res.writeHead(status, {Location: withoutSlash})
|
||||
res.end()
|
||||
return true
|
||||
}
|
||||
}
|
41
package-lock.json
generated
41
package-lock.json
generated
@ -1366,9 +1366,9 @@
|
||||
}
|
||||
},
|
||||
"@primer/blueprints": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@primer/blueprints/-/blueprints-3.0.1.tgz",
|
||||
"integrity": "sha512-B+YBA/S9GpkdC2PqKHsLqbjIXf7MjO+6Cl7jgDyvI6dg3PuNld7JmZSAGzV9NPbK6gI9F4yI9KIl4iSVPjSBgw==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@primer/blueprints/-/blueprints-3.0.2.tgz",
|
||||
"integrity": "sha512-mEx3F1Fl8lrZTfahgG2mTwm51OQbbgxH95GcShHIQBxg/hhdV6ce11/01geeMf8P8I8okvBD+Wj88Arkcor30A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@githubprimer/octicons-react": "^8.1.3",
|
||||
@ -1376,15 +1376,13 @@
|
||||
"@primer/next-pages": "0.0.3",
|
||||
"downshift": "3.2.7",
|
||||
"globby": "9.1.0",
|
||||
"html-2-jsx": "^0.5.1-dev",
|
||||
"lunr": "2.3.6",
|
||||
"prism-github": "^1.1.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "16.8.0",
|
||||
"react-dom": "16.8.1",
|
||||
"react-live": "2.0.0",
|
||||
"react-measure": "^2.2.2",
|
||||
"slugify": "1.3.4",
|
||||
"styled-components": "4.1.3",
|
||||
"styled-system": "4.0.4",
|
||||
"val-loader": "1.1.1"
|
||||
},
|
||||
@ -1411,18 +1409,6 @@
|
||||
"slash": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.8.0.tgz",
|
||||
"integrity": "sha512-g+nikW2D48kqgWSPwNo0NH9tIGG3DsQFlrtrQ1kj6W77z5ahyIHG0w8kPpz4Sdj6gyLnz0lEd/xsjOoGge2MYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.13.0"
|
||||
}
|
||||
},
|
||||
"react-live": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-live/-/react-live-2.0.0.tgz",
|
||||
@ -1438,25 +1424,6 @@
|
||||
"react-simple-code-editor": "^0.9.0",
|
||||
"unescape": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"styled-components": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.1.3.tgz",
|
||||
"integrity": "sha512-0quV4KnSfvq5iMtT0RzpMGl/Dg3XIxIxOl9eJpiqiq4SrAmR1l1DLzNpMzoy3DyzdXVDMJS2HzROnXscWA3SEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"@emotion/is-prop-valid": "^0.7.3",
|
||||
"@emotion/unitless": "^0.7.0",
|
||||
"babel-plugin-styled-components": ">= 1",
|
||||
"css-to-react-native": "^2.2.2",
|
||||
"memoize-one": "^4.0.0",
|
||||
"prop-types": "^15.5.4",
|
||||
"react-is": "^16.6.0",
|
||||
"stylis": "^3.5.0",
|
||||
"stylis-rule-sheet": "^0.0.10",
|
||||
"supports-color": "^5.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
16
package.json
16
package.json
@ -47,6 +47,9 @@
|
||||
"@githubprimer/octicons-react": "^8.1.3",
|
||||
"@mdx-js/mdx": "^0.16.6",
|
||||
"@mdx-js/tag": "0.15.0",
|
||||
"@primer/blueprints": "3.0.2",
|
||||
"@primer/components": "12.0.1",
|
||||
"@primer/next-pages": "0.0.3",
|
||||
"@storybook/addon-viewport": "5.0.0",
|
||||
"@storybook/react": "5.0.0",
|
||||
"@svgr/webpack": "2.4.1",
|
||||
@ -67,6 +70,7 @@
|
||||
"eslint": "4.19.1",
|
||||
"eslint-plugin-github": "1.0.0",
|
||||
"execa": "^0.10.0",
|
||||
"fs": "0.0.1-security",
|
||||
"fs-extra": "^4.0.2",
|
||||
"fx": "11.0.1",
|
||||
"gh-pages": "^1.0.0",
|
||||
@ -74,6 +78,7 @@
|
||||
"gray-matter": "^4.0.1",
|
||||
"hast-util-to-html": "^5.0.0",
|
||||
"hast-util-to-string": "^1.0.1",
|
||||
"html-2-jsx": "0.5.1-dev",
|
||||
"html-to-react": "^1.2.11",
|
||||
"klaw": "3.0.0",
|
||||
"loader-utils": "^1.1.0",
|
||||
@ -94,7 +99,9 @@
|
||||
"prism-github": "^1.1.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react": "16.8.1",
|
||||
"react-bodymovin": "2.0.0",
|
||||
"react-dom": "16.8.1",
|
||||
"react-live": "1.12.0",
|
||||
"react-measure": "^2.2.2",
|
||||
"refractor": "^2.6.2",
|
||||
@ -117,13 +124,6 @@
|
||||
"unist-util-select": "^2.0.0",
|
||||
"unist-util-stringify-position": "^2.0.0",
|
||||
"unist-util-visit": "^1.4.0",
|
||||
"webpack": "4.20.2",
|
||||
"@primer/components": "12.0.1",
|
||||
"@primer/next-pages": "0.0.3",
|
||||
"fs": "0.0.1-security",
|
||||
"html-2-jsx": "0.5.1-dev",
|
||||
"react": "16.8.1",
|
||||
"react-dom": "16.8.1",
|
||||
"@primer/blueprints": "3.0.1"
|
||||
"webpack": "4.20.2"
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import App, {Container} from 'next/app'
|
||||
import {MDXProvider} from '@mdx-js/tag'
|
||||
import Head from 'next/head'
|
||||
import {BaseStyles, BorderBox, Box, Flex, theme} from '@primer/components'
|
||||
import {Header, JumpNav, Section, RouteMatch, SectionLink, PackageHeader, SideNav} from '../docs/components'
|
||||
import {PackageHeader} from '../docs/components'
|
||||
import {Header, JumpNav, Section, RouteMatch, SectionLink, SideNav} from '@primer/blueprints'
|
||||
import getComponents from '../docs/markdown'
|
||||
import documents from '../searchIndex'
|
||||
import {config, requirePage, rootPage} from '../docs/utils'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import {Heading} from '@primer/components'
|
||||
import {redirectTrailingSlash} from '../docs/redirect'
|
||||
import {redirectTrailingSlash} from '@primer/blueprints'
|
||||
|
||||
export default class extends React.Component {
|
||||
static getInitialProps(context) {
|
||||
|
Loading…
Reference in New Issue
Block a user