Rework styling using styled-components (#2517)
The CSS in the project is relatively difficult to change. While it is very DRY, it relies heavily on inheritance. It's difficult to make changes in the markup modal without it also affecting styles elsewhere. [styled-components](https://www.styled-components.com/) is one of the leading CSS-in-JS libraries. By reducing dependency on global state and CSS inheritance, styles become explicit and are easier to inspect and change. It's also convenient that styles can be embedded with the components they modify. At runtime, the library creates CSS classes, so it's pretty efficient. We were using a little bit of [styled-jsx](https://github.com/zeit/styled-jsx) before, which ships with Next.js, though styled-components is more widely used and I've had good experiences with it all around. In a few cases I've duplicated styles where it feels more natural to do that: for example, `text-align: center` is duplicated in `Main` and `MarkupModal`. Much of this is a refactor, though there are a few visual changes, particularly in the markup modal and the style examples.
This commit is contained in:
@@ -35,7 +35,8 @@ const helperTests = fileMatch('lib/**/*.spec.js')
|
||||
const packageJson = fileMatch('package.json')
|
||||
const packageLock = fileMatch('package-lock.json')
|
||||
const capitals = fileMatch('**/*[A-Z]*.js')
|
||||
const underscores = fileMatch('**/*_*.js')
|
||||
// _document.js is used by convention by Next.
|
||||
const underscores = fileMatch('**/*_*.js', '!pages/_document.js')
|
||||
const targetBranch = danger.github.pr.base.ref
|
||||
|
||||
message(
|
||||
|
||||
@@ -1,6 +1,27 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import { badgeUrlFromPath, staticBadgeUrl } from '../../lib/make-badge-url'
|
||||
import { Badge } from './common'
|
||||
import { StyledCode } from './snippet'
|
||||
|
||||
const ExampleTable = styled.table`
|
||||
min-width: 50%;
|
||||
margin: auto;
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
`
|
||||
|
||||
const ClickableTh = styled.th`
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
const ClickableCode = styled(StyledCode)`
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
export default class BadgeExamples extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -47,21 +68,12 @@ export default class BadgeExamples extends React.Component {
|
||||
|
||||
return (
|
||||
<tr key={key}>
|
||||
<th className="clickable" onClick={handleClick}>
|
||||
{title}:
|
||||
</th>
|
||||
<ClickableTh onClick={handleClick}>{title}:</ClickableTh>
|
||||
<td>
|
||||
<img
|
||||
className="badge-img clickable"
|
||||
onClick={handleClick}
|
||||
src={previewUrl}
|
||||
alt=""
|
||||
/>
|
||||
<Badge clickable onClick={handleClick} src={previewUrl} />
|
||||
</td>
|
||||
<td>
|
||||
<code className="clickable" onClick={handleClick}>
|
||||
{exampleUrl}
|
||||
</code>
|
||||
<ClickableCode onClick={handleClick}>{exampleUrl}</ClickableCode>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
@@ -80,13 +92,11 @@ export default class BadgeExamples extends React.Component {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<table className="badge">
|
||||
<tbody>
|
||||
{flattened.map(exampleData => this.renderExample(exampleData))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<ExampleTable>
|
||||
<tbody>
|
||||
{flattened.map(exampleData => this.renderExample(exampleData))}
|
||||
</tbody>
|
||||
</ExampleTable>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { H3 } from './common'
|
||||
|
||||
const CategoryHeading = ({ category }) => {
|
||||
const { id, name } = category
|
||||
|
||||
return (
|
||||
<Link to={`/examples/${id}`}>
|
||||
<h3 id={id}>{name}</h3>
|
||||
<H3 id={id}>{name}</H3>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { shallow } from 'enzyme'
|
||||
import { expect } from 'chai'
|
||||
import { CategoryHeading, CategoryHeadings } from './category-headings'
|
||||
import { H3 } from './common'
|
||||
|
||||
import './enzyme-conf.spec'
|
||||
|
||||
@@ -14,7 +15,7 @@ describe('<CategoryHeading />', function() {
|
||||
|
||||
it('contains the expected heading', function() {
|
||||
const wrapper = shallow(<CategoryHeading category={exampleCategories[0]} />)
|
||||
expect(wrapper).to.contain(<h3 id="cat">Example category</h3>)
|
||||
expect(wrapper).to.contain(<H3 id="cat">Example category</H3>)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
97
frontend/components/common.js
Normal file
97
frontend/components/common.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import React from 'react'
|
||||
import styled, { css } from 'styled-components'
|
||||
|
||||
const nonBreakingSpace = '\u00a0'
|
||||
|
||||
const BaseFont = styled.div`
|
||||
font-family: Lekton, sans-serif;
|
||||
color: #534;
|
||||
`
|
||||
|
||||
const H2 = styled.h2`
|
||||
font-style: italic;
|
||||
|
||||
margin-top: 12mm;
|
||||
font-variant: small-caps;
|
||||
|
||||
::before {
|
||||
content: '☙ ';
|
||||
}
|
||||
|
||||
::after {
|
||||
content: ' ❧';
|
||||
}
|
||||
`
|
||||
|
||||
const H3 = styled.h3`
|
||||
font-style: italic;
|
||||
`
|
||||
|
||||
const BadgeWrapper = styled.span`
|
||||
padding: 2px;
|
||||
height: ${({ height }) => height};
|
||||
vertical-align: middle;
|
||||
display: ${({ display }) => display};
|
||||
|
||||
${({ clickable }) =>
|
||||
clickable &&
|
||||
css`
|
||||
cursor: pointer;
|
||||
`};
|
||||
`
|
||||
|
||||
const Badge = ({
|
||||
src,
|
||||
alt = '',
|
||||
display = 'inline',
|
||||
height = '20px',
|
||||
clickable = false,
|
||||
...rest
|
||||
}) => (
|
||||
<BadgeWrapper height={height} clickable={clickable} display={display}>
|
||||
{src ? <img src={src} alt={alt} {...rest} /> : nonBreakingSpace}
|
||||
</BadgeWrapper>
|
||||
)
|
||||
|
||||
const StyledInput = styled.input`
|
||||
height: 15px;
|
||||
border: solid #b9a;
|
||||
border-width: 0 0 1px 0;
|
||||
padding: 0;
|
||||
|
||||
text-align: center;
|
||||
|
||||
color: #534;
|
||||
|
||||
:focus {
|
||||
outline: 0;
|
||||
}
|
||||
`
|
||||
|
||||
const InlineInput = styled(StyledInput)`
|
||||
width: 70px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
`
|
||||
|
||||
const BlockInput = styled(StyledInput)`
|
||||
width: 40%;
|
||||
background-color: transparent;
|
||||
`
|
||||
|
||||
const VerticalSpace = styled.hr`
|
||||
border: 0;
|
||||
display: block;
|
||||
height: 3mm;
|
||||
`
|
||||
|
||||
export {
|
||||
nonBreakingSpace,
|
||||
BaseFont,
|
||||
H2,
|
||||
H3,
|
||||
Badge,
|
||||
InlineInput,
|
||||
BlockInput,
|
||||
VerticalSpace,
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { dynamicBadgeUrl } from '../lib/badge-url'
|
||||
import { InlineInput } from './common'
|
||||
|
||||
export default class DynamicBadgeMaker extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -52,49 +53,42 @@ export default class DynamicBadgeMaker extends React.Component {
|
||||
<option value="xml">xml</option>
|
||||
<option value="yaml">yaml</option>
|
||||
</select>{' '}
|
||||
{}
|
||||
<input
|
||||
<InlineInput
|
||||
className="short"
|
||||
value={this.state.label}
|
||||
onChange={event => this.setState({ label: event.target.value })}
|
||||
placeholder="label"
|
||||
/>{' '}
|
||||
{}
|
||||
<input
|
||||
/>
|
||||
<InlineInput
|
||||
className="short"
|
||||
value={this.state.url}
|
||||
onChange={event => this.setState({ url: event.target.value })}
|
||||
placeholder="url"
|
||||
/>{' '}
|
||||
{}
|
||||
<input
|
||||
/>
|
||||
<InlineInput
|
||||
className="short"
|
||||
value={this.state.query}
|
||||
onChange={event => this.setState({ query: event.target.value })}
|
||||
placeholder="query"
|
||||
/>{' '}
|
||||
{}
|
||||
<input
|
||||
/>
|
||||
<InlineInput
|
||||
className="short"
|
||||
value={this.state.color}
|
||||
onChange={event => this.setState({ color: event.target.value })}
|
||||
placeholder="color"
|
||||
/>{' '}
|
||||
{}
|
||||
<input
|
||||
/>
|
||||
<InlineInput
|
||||
className="short"
|
||||
value={this.state.prefix}
|
||||
onChange={event => this.setState({ prefix: event.target.value })}
|
||||
placeholder="prefix"
|
||||
/>{' '}
|
||||
{}
|
||||
<input
|
||||
/>
|
||||
<InlineInput
|
||||
className="short"
|
||||
value={this.state.suffix}
|
||||
onChange={event => this.setState({ suffix: event.target.value })}
|
||||
placeholder="suffix"
|
||||
/>{' '}
|
||||
{}
|
||||
/>
|
||||
<button disabled={!this.isValid}>Make Badge</button>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import resolveUrl from '../lib/resolve-url'
|
||||
import { H2 } from './common'
|
||||
|
||||
const SpacedA = styled.a`
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
`
|
||||
|
||||
const Footer = ({ baseUrl }) => (
|
||||
<section>
|
||||
<h2 id="like-this">Like This?</h2>
|
||||
<H2 id="like-this">Like This?</H2>
|
||||
|
||||
<p>
|
||||
<object
|
||||
@@ -49,9 +56,9 @@ const Footer = ({ baseUrl }) => (
|
||||
and we might bring it to you!
|
||||
</p>
|
||||
|
||||
<p className="spaced-row">
|
||||
<a href="https://status.shields.io/">Status</a>
|
||||
<a href="https://github.com/badges/shields/">GitHub</a>
|
||||
<p>
|
||||
<SpacedA href="https://status.shields.io/">Status</SpacedA>
|
||||
<SpacedA href="https://github.com/badges/shields/">GitHub</SpacedA>
|
||||
</p>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { shallow } from 'enzyme'
|
||||
import { shallow, render } from 'enzyme'
|
||||
import { expect } from 'chai'
|
||||
import Footer from './footer'
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('<Footer />', function() {
|
||||
})
|
||||
|
||||
it('contains a link to the status page', function() {
|
||||
const wrapper = shallow(<Footer baseUrl="https://example.shields.io" />)
|
||||
expect(wrapper).to.contain(<a href="https://status.shields.io/">Status</a>)
|
||||
const wrapper = render(<Footer baseUrl="https://example.shields.io" />)
|
||||
expect(wrapper.html()).to.contain('https://status.shields.io/')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { VerticalSpace } from './common'
|
||||
|
||||
const Highlights = styled.p`
|
||||
font-style: italic;
|
||||
`
|
||||
|
||||
export default () => (
|
||||
<section>
|
||||
@@ -7,17 +13,11 @@ export default () => (
|
||||
<img alt="Shields.io" src="/static/logo.svg" />
|
||||
</Link>
|
||||
|
||||
<hr className="spacing" />
|
||||
<VerticalSpace />
|
||||
|
||||
<p className="highlights">
|
||||
<Highlights>
|
||||
Pixel-perfect Retina-ready Fast Consistent
|
||||
Hackable No tracking
|
||||
</p>
|
||||
|
||||
<style jsx>{`
|
||||
.highlights {
|
||||
font-style: italic;
|
||||
}
|
||||
`}</style>
|
||||
</Highlights>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import groupBy from 'lodash.groupby'
|
||||
import {
|
||||
categories,
|
||||
findCategory,
|
||||
services,
|
||||
getDefinitionsForCategory,
|
||||
} from '../lib/service-definitions'
|
||||
import ServiceDefinitionSetHelper from '../lib/service-definitions/service-definition-set-helper'
|
||||
import { baseUrl, longCache } from '../constants'
|
||||
import Meta from './meta'
|
||||
import Header from './header'
|
||||
import SuggestionAndSearch from './suggestion-and-search'
|
||||
@@ -8,16 +18,12 @@ import MarkupModal from './markup-modal'
|
||||
import Usage from './usage'
|
||||
import Footer from './footer'
|
||||
import { CategoryHeading, CategoryHeadings } from './category-headings'
|
||||
import {
|
||||
categories,
|
||||
findCategory,
|
||||
services,
|
||||
getDefinitionsForCategory,
|
||||
} from '../lib/service-definitions'
|
||||
import BadgeExamples from './badge-examples'
|
||||
import { baseUrl, longCache } from '../constants'
|
||||
import ServiceDefinitionSetHelper from '../lib/service-definitions/service-definition-set-helper'
|
||||
import groupBy from 'lodash.groupby'
|
||||
import { BaseFont } from './common'
|
||||
|
||||
const AppContainer = styled(BaseFont)`
|
||||
text-align: center;
|
||||
`
|
||||
|
||||
export default class Main extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -140,7 +146,7 @@ export default class Main extends React.Component {
|
||||
const { selectedExample } = this.state
|
||||
|
||||
return (
|
||||
<div id="app">
|
||||
<AppContainer id="app">
|
||||
<Meta />
|
||||
<Header />
|
||||
<MarkupModal
|
||||
@@ -161,7 +167,7 @@ export default class Main extends React.Component {
|
||||
{this.renderMain()}
|
||||
<Usage baseUrl={baseUrl} longCache={longCache} />
|
||||
<Footer baseUrl={baseUrl} />
|
||||
</div>
|
||||
</AppContainer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal from 'react-modal'
|
||||
import ClickToSelect from '@mapbox/react-click-to-select'
|
||||
import styled from 'styled-components'
|
||||
import { badgeUrlFromPath, badgeUrlFromPattern } from '../../lib/make-badge-url'
|
||||
import generateAllMarkup from '../lib/generate-image-markup'
|
||||
import { advertisedStyles } from '../../supported-features.json'
|
||||
import { Snippet } from './snippet'
|
||||
import { BaseFont, H3, Badge, BlockInput } from './common'
|
||||
|
||||
const nonBreakingSpace = '\u00a0'
|
||||
const ContentContainer = styled(BaseFont)`
|
||||
text-align: center;
|
||||
`
|
||||
|
||||
const WeeSnippet = ({ snippet, truncate = false }) => (
|
||||
<Snippet truncate={truncate} fontSize="10pt" snippet={snippet} />
|
||||
)
|
||||
WeeSnippet.propTypes = {
|
||||
snippet: PropTypes.string.isRequired,
|
||||
truncate: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default class MarkupModal extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -100,13 +112,13 @@ export default class MarkupModal extends React.Component {
|
||||
renderLivePreview() {
|
||||
const { badgeUrl } = this.state
|
||||
const includesPlaceholders = badgeUrl.includes(':')
|
||||
|
||||
if (includesPlaceholders) {
|
||||
return nonBreakingSpace
|
||||
let src
|
||||
if (badgeUrl && !includesPlaceholders) {
|
||||
src = this.generateBuiltBadgeUrl()
|
||||
} else {
|
||||
const livePreviewUrl = this.generateBuiltBadgeUrl()
|
||||
return <img className="badge-img" src={livePreviewUrl} />
|
||||
src = undefined
|
||||
}
|
||||
return <Badge display="block" src={src} />
|
||||
}
|
||||
|
||||
renderMarkup() {
|
||||
@@ -128,31 +140,19 @@ export default class MarkupModal extends React.Component {
|
||||
<div>
|
||||
<p>
|
||||
URL
|
||||
<ClickToSelect>
|
||||
<input className="code clickable" readOnly value={builtBadgeUrl} />
|
||||
</ClickToSelect>
|
||||
<WeeSnippet snippet={builtBadgeUrl} />
|
||||
</p>
|
||||
<p>
|
||||
Markdown
|
||||
<ClickToSelect>
|
||||
<input className="code clickable" readOnly value={markdown} />
|
||||
</ClickToSelect>
|
||||
<WeeSnippet truncate snippet={markdown} />
|
||||
</p>
|
||||
<p>
|
||||
reStructuredText
|
||||
<ClickToSelect>
|
||||
<input
|
||||
className="code clickable"
|
||||
readOnly
|
||||
value={reStructuredText}
|
||||
/>
|
||||
</ClickToSelect>
|
||||
<WeeSnippet truncate snippet={reStructuredText} />
|
||||
</p>
|
||||
<p>
|
||||
AsciiDoc
|
||||
<ClickToSelect>
|
||||
<input className="code clickable" readOnly value={asciiDoc} />
|
||||
</ClickToSelect>
|
||||
<WeeSnippet truncate snippet={asciiDoc} />
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
@@ -173,7 +173,7 @@ export default class MarkupModal extends React.Component {
|
||||
|
||||
render() {
|
||||
const { isOpen } = this
|
||||
const { onRequestClose } = this.props
|
||||
const { onRequestClose, example: { title } = {} } = this.props
|
||||
const { link, badgeUrl, exampleUrl, style } = this.state
|
||||
|
||||
const common = {
|
||||
@@ -190,62 +190,63 @@ export default class MarkupModal extends React.Component {
|
||||
contentLabel="Example Modal"
|
||||
ariaHideApp={false}
|
||||
>
|
||||
<form action="">
|
||||
<p>{isOpen && this.renderLivePreview()}</p>
|
||||
<p>
|
||||
<label>
|
||||
Link
|
||||
<input
|
||||
type="url"
|
||||
value={link}
|
||||
onChange={event => {
|
||||
this.setState({ link: event.target.value })
|
||||
}}
|
||||
{...common}
|
||||
/>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Path
|
||||
<input
|
||||
type="url"
|
||||
value={badgeUrl}
|
||||
onChange={event => {
|
||||
this.setState({ badgeUrl: event.target.value })
|
||||
}}
|
||||
{...common}
|
||||
/>
|
||||
</label>
|
||||
</p>
|
||||
{exampleUrl && (
|
||||
<ContentContainer>
|
||||
<form action="">
|
||||
<H3>{title}</H3>
|
||||
{isOpen && this.renderLivePreview()}
|
||||
<p>
|
||||
Example
|
||||
<ClickToSelect>
|
||||
<input className="code clickable" readOnly value={exampleUrl} />
|
||||
</ClickToSelect>
|
||||
<label>
|
||||
Link
|
||||
<BlockInput
|
||||
type="url"
|
||||
value={link}
|
||||
onChange={event => {
|
||||
this.setState({ link: event.target.value })
|
||||
}}
|
||||
{...common}
|
||||
/>
|
||||
</label>
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
<label>
|
||||
Style
|
||||
<select
|
||||
value={style}
|
||||
onChange={event => {
|
||||
this.setState({ style: event.target.value })
|
||||
}}
|
||||
>
|
||||
{advertisedStyles.map(style => (
|
||||
<option key={style} value={style}>
|
||||
{style}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
{isOpen && this.renderMarkup()}
|
||||
{isOpen && this.renderDocumentation()}
|
||||
</form>
|
||||
<p>
|
||||
<label>
|
||||
Path
|
||||
<BlockInput
|
||||
type="url"
|
||||
value={badgeUrl}
|
||||
onChange={event => {
|
||||
this.setState({ badgeUrl: event.target.value })
|
||||
}}
|
||||
{...common}
|
||||
/>
|
||||
</label>
|
||||
</p>
|
||||
{exampleUrl && (
|
||||
<p>
|
||||
Example
|
||||
<Snippet fontSize="10pt" snippet={exampleUrl} />
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
<label>
|
||||
Style
|
||||
<select
|
||||
value={style}
|
||||
onChange={event => {
|
||||
this.setState({ style: event.target.value })
|
||||
}}
|
||||
>
|
||||
{advertisedStyles.map(style => (
|
||||
<option key={style} value={style}>
|
||||
{style}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
{isOpen && this.renderMarkup()}
|
||||
{isOpen && this.renderDocumentation()}
|
||||
</form>
|
||||
</ContentContainer>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ export default () => (
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="description" content={description} />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link href="/static/main.css" rel="stylesheet" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Lekton"
|
||||
rel="stylesheet"
|
||||
|
||||
45
frontend/components/snippet.js
Normal file
45
frontend/components/snippet.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ClickToSelect from '@mapbox/react-click-to-select'
|
||||
import styled, { css } from 'styled-components'
|
||||
|
||||
const CodeContainer = styled.span`
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
|
||||
${({ truncate }) =>
|
||||
truncate &&
|
||||
css`
|
||||
max-width: 40%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`};
|
||||
`
|
||||
|
||||
const StyledCode = styled.code`
|
||||
line-height: 1.2em;
|
||||
padding: 0.1em 0.3em;
|
||||
|
||||
border-radius: 4px;
|
||||
background: #eef;
|
||||
|
||||
font-family: Lekton;
|
||||
font-size: ${({ fontSize }) => fontSize};
|
||||
|
||||
white-space: nowrap;
|
||||
`
|
||||
|
||||
const Snippet = ({ snippet, truncate = false, fontSize }) => (
|
||||
<CodeContainer truncate={truncate}>
|
||||
<ClickToSelect>
|
||||
<StyledCode fontSize={fontSize}>{snippet}</StyledCode>
|
||||
</ClickToSelect>
|
||||
</CodeContainer>
|
||||
)
|
||||
Snippet.propTypes = {
|
||||
snippet: PropTypes.string.isRequired,
|
||||
truncate: PropTypes.bool,
|
||||
fontSize: PropTypes.string,
|
||||
}
|
||||
|
||||
export { Snippet, StyledCode }
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { staticBadgeUrl } from '../lib/badge-url'
|
||||
import { InlineInput } from './common'
|
||||
|
||||
export default class StaticBadgeMaker extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -31,28 +32,22 @@ export default class StaticBadgeMaker extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<form onSubmit={e => this.handleSubmit(e)}>
|
||||
<input
|
||||
className="short"
|
||||
<InlineInput
|
||||
value={this.state.subject}
|
||||
onChange={event => this.setState({ subject: event.target.value })}
|
||||
placeholder="subject"
|
||||
/>{' '}
|
||||
{}
|
||||
<input
|
||||
className="short"
|
||||
/>
|
||||
<InlineInput
|
||||
value={this.state.status}
|
||||
onChange={event => this.setState({ status: event.target.value })}
|
||||
placeholder="status"
|
||||
/>{' '}
|
||||
{}
|
||||
<input
|
||||
className="short"
|
||||
/>
|
||||
<InlineInput
|
||||
value={this.state.color}
|
||||
onChange={event => this.setState({ color: event.target.value })}
|
||||
list="default-colors"
|
||||
placeholder="color"
|
||||
/>{' '}
|
||||
{}
|
||||
/>
|
||||
<datalist id="default-colors">
|
||||
<option value="brightgreen" />
|
||||
<option value="green" />
|
||||
@@ -62,8 +57,7 @@ export default class StaticBadgeMaker extends React.Component {
|
||||
<option value="red" />
|
||||
<option value="lightgrey" />
|
||||
<option value="blue" />
|
||||
</datalist>{' '}
|
||||
{}
|
||||
</datalist>
|
||||
<button>Make Badge</button>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -2,8 +2,9 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import fetchPonyfill from 'fetch-ponyfill'
|
||||
import debounce from 'lodash.debounce'
|
||||
import BadgeExamples from './badge-examples'
|
||||
import resolveUrl from '../lib/resolve-url'
|
||||
import BadgeExamples from './badge-examples'
|
||||
import { BlockInput } from './common'
|
||||
|
||||
export default class SuggestionAndSearch extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -93,7 +94,7 @@ export default class SuggestionAndSearch extends React.Component {
|
||||
return (
|
||||
<section>
|
||||
<form action="javascript:void 0" autoComplete="off">
|
||||
<input
|
||||
<BlockInput
|
||||
onChange={event => this.queryChanged(event.target.value)}
|
||||
autofill="off"
|
||||
autoFocus
|
||||
|
||||
@@ -1,9 +1,66 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import StaticBadgeMaker from './static-badge-maker'
|
||||
import DynamicBadgeMaker from './dynamic-badge-maker'
|
||||
import styled from 'styled-components'
|
||||
import { staticBadgeUrl } from '../lib/badge-url'
|
||||
import { advertisedStyles, logos } from '../../supported-features.json'
|
||||
import StaticBadgeMaker from './static-badge-maker'
|
||||
import DynamicBadgeMaker from './dynamic-badge-maker'
|
||||
import { H2, H3, Badge, VerticalSpace } from './common'
|
||||
import { Snippet, StyledCode } from './snippet'
|
||||
|
||||
const LogoName = styled.span`
|
||||
white-space: nowrap;
|
||||
`
|
||||
|
||||
const Lhs = styled.td`
|
||||
text-align: right;
|
||||
`
|
||||
|
||||
const EscapingRuleTable = styled.table`
|
||||
margin: auto;
|
||||
`
|
||||
|
||||
const QueryParamTable = styled.table`
|
||||
min-width: 50%;
|
||||
margin: auto;
|
||||
table-layout: fixed;
|
||||
border-spacing: 20px 10px;
|
||||
`
|
||||
|
||||
const QueryParamSyntax = styled.td`
|
||||
max-width: 300px;
|
||||
text-align: left;
|
||||
`
|
||||
|
||||
const QueryParamDocumentation = styled.td`
|
||||
max-width: 600px;
|
||||
text-align: left;
|
||||
`
|
||||
|
||||
const QueryParam = ({ snippet, documentation }) => (
|
||||
<tr>
|
||||
<QueryParamSyntax>
|
||||
<Snippet snippet={snippet} />
|
||||
</QueryParamSyntax>
|
||||
<QueryParamDocumentation>{documentation}</QueryParamDocumentation>
|
||||
</tr>
|
||||
)
|
||||
QueryParam.propTypes = {
|
||||
snippet: PropTypes.string.isRequired,
|
||||
documentation: PropTypes.element.isRequired,
|
||||
}
|
||||
|
||||
const EscapingConversion = ({ lhs, rhs }) => (
|
||||
<tr>
|
||||
<Lhs>{lhs}</Lhs>
|
||||
<td>→</td>
|
||||
<td>{rhs}</td>
|
||||
</tr>
|
||||
)
|
||||
EscapingConversion.propTypes = {
|
||||
lhs: PropTypes.element.isRequired,
|
||||
rhs: PropTypes.element.isRequired,
|
||||
}
|
||||
|
||||
export default class Usage extends React.PureComponent {
|
||||
static propTypes = {
|
||||
@@ -27,12 +84,10 @@ export default class Usage extends React.PureComponent {
|
||||
<p>
|
||||
{colors.map((color, i) => (
|
||||
<Fragment key={i}>
|
||||
<img
|
||||
className="badge-img"
|
||||
<Badge
|
||||
src={staticBadgeUrl(baseUrl, 'color', color, color)}
|
||||
alt={color}
|
||||
/>{' '}
|
||||
{}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</p>
|
||||
@@ -42,98 +97,108 @@ export default class Usage extends React.PureComponent {
|
||||
renderStyleExamples() {
|
||||
const { baseUrl } = this.props
|
||||
return (
|
||||
<table className="badge-img">
|
||||
<QueryParamTable>
|
||||
<tbody>
|
||||
{advertisedStyles.map((style, i) => {
|
||||
{advertisedStyles.map(style => {
|
||||
const snippet = `?style=${style}&logo=appveyor`
|
||||
const badgeUrl = staticBadgeUrl(baseUrl, 'style', style, 'green', {
|
||||
logo: 'appveyor',
|
||||
style,
|
||||
})
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<img className="badge-img" src={badgeUrl} alt={style} />
|
||||
</td>
|
||||
<td>
|
||||
<code>{badgeUrl}</code>
|
||||
</td>
|
||||
</tr>
|
||||
<QueryParam
|
||||
key={style}
|
||||
snippet={snippet}
|
||||
documentation={<Badge src={badgeUrl} alt={style} />}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</QueryParamTable>
|
||||
)
|
||||
}
|
||||
|
||||
static renderNamedLogos() {
|
||||
const renderLogo = logo => (
|
||||
<span className="nowrap" key={logo}>
|
||||
{logo}
|
||||
</span>
|
||||
)
|
||||
const renderLogo = logo => <LogoName key={logo}>{logo}</LogoName>
|
||||
const [first, ...rest] = logos
|
||||
return [renderLogo(first)].concat(
|
||||
rest.reduce((result, logo) => result.concat([', ', renderLogo(logo)]), [])
|
||||
)
|
||||
}
|
||||
|
||||
static renderStaticBadgeEscapingRules() {
|
||||
return (
|
||||
<EscapingRuleTable>
|
||||
<tbody>
|
||||
<EscapingConversion
|
||||
key="dashes"
|
||||
lhs={
|
||||
<span>
|
||||
Dashes <code>--</code>
|
||||
</span>
|
||||
}
|
||||
rhs={
|
||||
<span>
|
||||
<code>-</code> Dash
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<EscapingConversion
|
||||
key="underscores"
|
||||
lhs={
|
||||
<span>
|
||||
Underscores <code>__</code>
|
||||
</span>
|
||||
}
|
||||
rhs={
|
||||
<span>
|
||||
<code>_</code> Underscore
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<EscapingConversion
|
||||
key="spaces"
|
||||
lhs={
|
||||
<span>
|
||||
<code>_</code> or Space <code> </code>
|
||||
</span>
|
||||
}
|
||||
rhs={
|
||||
<span>
|
||||
<code> </code> Space
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</tbody>
|
||||
</EscapingRuleTable>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { baseUrl } = this.props
|
||||
return (
|
||||
<section>
|
||||
<h2 id="your-badge">Your Badge</h2>
|
||||
<H2 id="your-badge">Your Badge</H2>
|
||||
|
||||
<h3 id="static-badge">Static</h3>
|
||||
<H3 id="static-badge">Static</H3>
|
||||
<StaticBadgeMaker baseUrl={baseUrl} />
|
||||
|
||||
<hr className="spacing" />
|
||||
<VerticalSpace />
|
||||
|
||||
<p>
|
||||
<code>
|
||||
{baseUrl}
|
||||
/badge/<SUBJECT>-<STATUS>-<COLOR>.svg
|
||||
</code>
|
||||
<Snippet
|
||||
snippet={`${baseUrl}/badge/<SUBJECT>-<STATUS>-<COLOR>.svg`}
|
||||
/>
|
||||
</p>
|
||||
<table className="centered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
Dashes <code>--</code>
|
||||
</td>
|
||||
<td>→</td>
|
||||
<td>
|
||||
<code>-</code> Dash
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Underscores <code>__</code>
|
||||
</td>
|
||||
<td>→</td>
|
||||
<td>
|
||||
<code>_</code> Underscore
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>_</code> or Space <code> </code>
|
||||
</td>
|
||||
<td>→</td>
|
||||
<td>
|
||||
<code> </code> Space
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{this.constructor.renderStaticBadgeEscapingRules()}
|
||||
{this.renderColorExamples()}
|
||||
|
||||
<h3 id="dynamic-badge">Dynamic</h3>
|
||||
<H3 id="dynamic-badge">Dynamic</H3>
|
||||
|
||||
<DynamicBadgeMaker baseUrl={baseUrl} />
|
||||
|
||||
<p>
|
||||
<code>
|
||||
<StyledCode>
|
||||
{baseUrl}
|
||||
/badge/dynamic/json.svg?url=<URL>&label=<LABEL>&query=<
|
||||
<a
|
||||
@@ -144,10 +209,10 @@ export default class Usage extends React.PureComponent {
|
||||
$.DATA.SUBDATA
|
||||
</a>
|
||||
>&colorB=<COLOR>&prefix=<PREFIX>&suffix=<SUFFIX>
|
||||
</code>
|
||||
</StyledCode>
|
||||
</p>
|
||||
<p>
|
||||
<code>
|
||||
<StyledCode>
|
||||
{baseUrl}
|
||||
/badge/dynamic/xml.svg?url=<URL>&label=<LABEL>&query=<
|
||||
<a
|
||||
@@ -158,10 +223,10 @@ export default class Usage extends React.PureComponent {
|
||||
//data/subdata
|
||||
</a>
|
||||
>&colorB=<COLOR>&prefix=<PREFIX>&suffix=<SUFFIX>
|
||||
</code>
|
||||
</StyledCode>
|
||||
</p>
|
||||
<p>
|
||||
<code>
|
||||
<StyledCode>
|
||||
{baseUrl}
|
||||
/badge/dynamic/yaml.svg?url=<URL>&label=<LABEL>&query=<
|
||||
<a
|
||||
@@ -172,12 +237,12 @@ export default class Usage extends React.PureComponent {
|
||||
$.DATA.SUBDATA
|
||||
</a>
|
||||
>&colorB=<COLOR>&prefix=<PREFIX>&suffix=<SUFFIX>
|
||||
</code>
|
||||
</StyledCode>
|
||||
</p>
|
||||
|
||||
<hr className="spacing" />
|
||||
<VerticalSpace />
|
||||
|
||||
<h2 id="styles">Styles</h2>
|
||||
<H2 id="styles">Styles</H2>
|
||||
|
||||
<p>
|
||||
The following styles are available. Flat is the default. Examples are
|
||||
@@ -189,92 +254,101 @@ export default class Usage extends React.PureComponent {
|
||||
Here are a few other parameters you can use: (connecting several with
|
||||
"&" is possible)
|
||||
</p>
|
||||
<table className="usage">
|
||||
<QueryParamTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?label=healthinesses</code>
|
||||
</td>
|
||||
<td>
|
||||
Override the default left-hand-side text (
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding">
|
||||
URL-Encoding
|
||||
</a>
|
||||
{} needed for spaces or special characters!)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?logo=appveyor</code>
|
||||
</td>
|
||||
<td>
|
||||
Insert one of the named logos from (
|
||||
{this.constructor.renderNamedLogos()}) or{' '}
|
||||
<a href="https://simpleicons.org/" target="_BLANK">
|
||||
simple-icons
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?logo=data:image/png;base64,…</code>
|
||||
</td>
|
||||
<td>Insert custom logo image (≥ 14px high)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?logoColor=violet</code>
|
||||
</td>
|
||||
<td>
|
||||
Set the color of the logo (hex, rgb, rgba, hsl, hsla and css
|
||||
named colors supported)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?logoWidth=40</code>
|
||||
</td>
|
||||
<td>Set the horizontal space to give to the logo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?link=http://left&link=http://right</code>
|
||||
</td>
|
||||
<td>
|
||||
Specify what clicking on the left/right of a badge should do
|
||||
(esp. for social badge style)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?colorA=abcdef</code>
|
||||
</td>
|
||||
<td>
|
||||
Set background of the left part (hex, rgb, rgba, hsl, hsla and
|
||||
css named colors supported)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?colorB=fedcba</code>
|
||||
</td>
|
||||
<td>
|
||||
Set background of the right part (hex, rgb, rgba, hsl, hsla and
|
||||
css named colors supported)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>?maxAge=3600</code>
|
||||
</td>
|
||||
<td>
|
||||
Set the HTTP cache lifetime in secs (rules are applied to infer
|
||||
a default value on a per-badge basis, any values specified below
|
||||
the default will be ignored)
|
||||
</td>
|
||||
</tr>
|
||||
<QueryParam
|
||||
key="label"
|
||||
snippet="?label=healthinesses"
|
||||
documentation={
|
||||
<span>
|
||||
Override the default left-hand-side text (
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding">
|
||||
URL-Encoding
|
||||
</a>
|
||||
{} needed for spaces or special characters!)
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="logo"
|
||||
snippet="?logo=appveyor"
|
||||
documentation={
|
||||
<span>
|
||||
Insert one of the named logos from (
|
||||
{this.constructor.renderNamedLogos()}) or{' '}
|
||||
<a href="https://simpleicons.org/" target="_BLANK">
|
||||
simple-icons
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="logoSvg"
|
||||
snippet="?logo=data:image/png;base64,…"
|
||||
documentation={
|
||||
<span>Insert custom logo image (≥ 14px high)</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="logoColor"
|
||||
snippet="?logoColor=violet"
|
||||
documentation={
|
||||
<span>
|
||||
Set the color of the logo (hex, rgb, rgba, hsl, hsla and css
|
||||
named colors supported)
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="logoWidth"
|
||||
snippet="?logoWidth=40"
|
||||
documentation={
|
||||
<span>Set the horizontal space to give to the logo</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="link"
|
||||
snippet="?link=http://left&link=http://right"
|
||||
documentation={
|
||||
<span>
|
||||
Specify what clicking on the left/right of a badge should do
|
||||
(esp. for social badge style)
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="colorA"
|
||||
snippet="?colorA=abcdef"
|
||||
documentation={
|
||||
<span>
|
||||
Set background of the left part (hex, rgb, rgba, hsl, hsla and
|
||||
css named colors supported)
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="colorB"
|
||||
snippet="?colorB=fedcba"
|
||||
documentation={
|
||||
<span>
|
||||
Set background of the right part (hex, rgb, rgba, hsl, hsla
|
||||
and css named colors supported)
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<QueryParam
|
||||
key="maxAge"
|
||||
snippet="?maxAge=3600"
|
||||
documentation={
|
||||
<span>
|
||||
Set the HTTP cache lifetime in secs (rules are applied to
|
||||
infer a default value on a per-badge basis, any values
|
||||
specified below the default will be ignored)
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</QueryParamTable>
|
||||
|
||||
<p>
|
||||
We support <code>.svg</code>, <code>.json</code>, <code>.png</code>{' '}
|
||||
|
||||
108
package-lock.json
generated
108
package-lock.json
generated
@@ -1213,6 +1213,27 @@
|
||||
"lazy-ass": "1.6.0"
|
||||
}
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "0.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz",
|
||||
"integrity": "sha512-IMSL7ekYhmFlILXcouA6ket3vV7u9BqStlXzbKOF9HBtpUPMMlHU+bBxrLOa2NvleVwNIxeq/zL8LafLbeUXcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@emotion/memoize": "^0.6.6"
|
||||
}
|
||||
},
|
||||
"@emotion/memoize": {
|
||||
"version": "0.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
|
||||
"integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@emotion/unitless": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz",
|
||||
"integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg==",
|
||||
"dev": true
|
||||
},
|
||||
"@iamstarkov/listr-update-renderer": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz",
|
||||
@@ -1865,6 +1886,17 @@
|
||||
"integrity": "sha1-Lk57RJa5OmVKHIAEInbeTk7rIOM=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-styled-components": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.9.2.tgz",
|
||||
"integrity": "sha512-McnheW8RkBkur/mQw7rEwQO/oUUruQ/nIIj5LIRpsVL8pzG1oo1Y53xyvAYeOfamIrl4/ta7g1G/kuTR1ekO3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.0.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"lodash": "^4.17.10"
|
||||
}
|
||||
},
|
||||
"babel-plugin-syntax-jsx": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
||||
@@ -3445,6 +3477,12 @@
|
||||
"randomfill": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"css-color-keywords": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||
"integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=",
|
||||
"dev": true
|
||||
},
|
||||
"css-select": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
|
||||
@@ -3461,6 +3499,17 @@
|
||||
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz",
|
||||
"integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA="
|
||||
},
|
||||
"css-to-react-native": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.2.tgz",
|
||||
"integrity": "sha512-w99Fzop1FO8XKm0VpbQp3y5mnTnaS+rtCvS+ylSEOK76YXO5zoHQx/QMB1N54Cp+Ya9jB9922EHrh14ld4xmmw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css-color-keywords": "^1.0.0",
|
||||
"fbjs": "^0.8.5",
|
||||
"postcss-value-parser": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"css-tree": {
|
||||
"version": "1.0.0-alpha.28",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz",
|
||||
@@ -8754,6 +8803,12 @@
|
||||
"integrity": "sha512-D2JKK2DTuVYQqquBWco3K6UfSVyVwmd58dgNqh+TgxHOZdTmR8I130gjMbVCkemDl/EzqDA62417cJxKL3/FFA==",
|
||||
"dev": true
|
||||
},
|
||||
"memoize-one": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.3.tgz",
|
||||
"integrity": "sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw==",
|
||||
"dev": true
|
||||
},
|
||||
"memory-fs": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
||||
@@ -12667,6 +12722,12 @@
|
||||
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
|
||||
"dev": true
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
|
||||
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
|
||||
"dev": true
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
@@ -14948,6 +15009,53 @@
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
|
||||
"dev": true
|
||||
},
|
||||
"styled-components": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.1.2.tgz",
|
||||
"integrity": "sha512-NdvWatJ2WLqZxAvto+oH0k7GAC/TlAUJTrHoXJddjbCrU6U23EmVbb9LXJBF+d6q6hH+g9nQYOWYPUeX/Vlc2w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@emotion/is-prop-valid": "^0.6.8",
|
||||
"@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"
|
||||
},
|
||||
"dependencies": {
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"dev": true
|
||||
},
|
||||
"stylis": {
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
|
||||
"integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==",
|
||||
"dev": true
|
||||
},
|
||||
"stylis-rule-sheet": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
||||
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"styled-jsx": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "http://registry.npmjs.org/styled-jsx/-/styled-jsx-2.2.6.tgz",
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
"sinon": "^7.2.2",
|
||||
"sinon-chai": "^3.3.0",
|
||||
"snap-shot-it": "^6.2.7",
|
||||
"styled-components": "^4.1.2",
|
||||
"tmp": "0.0.33",
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
|
||||
31
pages/_document.js
Normal file
31
pages/_document.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// https://www.styled-components.com/docs/advanced#nextjs
|
||||
|
||||
import Document, { Head, Main, NextScript } from 'next/document'
|
||||
import React from 'react'
|
||||
import { ServerStyleSheet } from 'styled-components'
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
static getInitialProps({ renderPage }) {
|
||||
const sheet = new ServerStyleSheet()
|
||||
const page = renderPage(App => props =>
|
||||
sheet.collectStyles(<App {...props} />)
|
||||
)
|
||||
const styleTags = sheet.getStyleElement()
|
||||
return { ...page, styleTags }
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<html>
|
||||
<Head>
|
||||
<title>My page</title>
|
||||
{this.props.styleTags}
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
}
|
||||
122
static/main.css
122
static/main.css
@@ -1,122 +0,0 @@
|
||||
html {
|
||||
background-attachment: fixed;
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDAiIGhlaWdodD0iNDgwIj48ZmlsdGVyIGlkPSJhIj48ZmVUdXJidWxlbmNlIGJhc2VGcmVxdWVuY3k9Ii4wOCIgbnVtT2N0YXZlcz0iOCIgc3RpdGNoVGlsZXM9InN0aXRjaCIgc2VlZD0iMzQ2Ii8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIxIDAgMCAwIDAgIDEgMCAwIDAgMCAgMSAwIDAgMCAuOSAgLjAxIDAgMCAwIC4wMSIvPjwvZmlsdGVyPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbHRlcj0idXJsKCNhKSIvPjwvc3ZnPg==);
|
||||
}
|
||||
|
||||
:root,
|
||||
dialog {
|
||||
text-align: center;
|
||||
font-family: Lekton, sans-serif;
|
||||
color: #534;
|
||||
}
|
||||
|
||||
code,
|
||||
.code {
|
||||
font-family: Lekton;
|
||||
white-space: pre-wrap;
|
||||
padding: 0 4px;
|
||||
background: #eef;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
input.short {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
input {
|
||||
text-align: center;
|
||||
border: solid #b9a;
|
||||
color: #534;
|
||||
border-width: 0 0 1px 0;
|
||||
width: 40%;
|
||||
height: 15px;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 40%;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
text-align: left;
|
||||
margin-left: 25%;
|
||||
}
|
||||
|
||||
table {
|
||||
min-width: 50%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
table.centered > tbody > tr > td:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.usage {
|
||||
table-layout: fixed;
|
||||
border-spacing: 20px 10px;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.usage td:first-of-type {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
table.usage td:nth-of-type(2) {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
h2::before {
|
||||
content: '☙ ';
|
||||
}
|
||||
|
||||
h2::after {
|
||||
content: ' ❧';
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 12mm;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
hr.spacing {
|
||||
border: 0;
|
||||
display: block;
|
||||
height: 3mm;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.excluded {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.badge-img {
|
||||
min-height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.spaced-row * {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
Reference in New Issue
Block a user