mirror of https://github.com/sgoudham/carbon.git
snippets and account pages
parent
5ebd355ef0
commit
31747420d1
@ -0,0 +1,277 @@
|
||||
import React from 'react'
|
||||
import { Elements, StripeProvider, CardElement, injectStripe } from 'react-stripe-elements'
|
||||
import { useAsyncCallback } from '@dawnlabs/tacklebox'
|
||||
|
||||
import Button from './Button'
|
||||
import Input from './Input'
|
||||
import { useAuth } from './AuthContext'
|
||||
import LoginButton from './LoginButton'
|
||||
|
||||
import { COLORS } from '../lib/constants'
|
||||
|
||||
const X = (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 17 17">
|
||||
<path
|
||||
className="base"
|
||||
fill="white"
|
||||
d="M8.5,17 C3.80557963,17 0,13.1944204 0,8.5 C0,3.80557963 3.80557963,0 8.5,0 C13.1944204,0 17,3.80557963 17,8.5 C17,13.1944204 13.1944204,17 8.5,17 Z"
|
||||
/>
|
||||
<path
|
||||
className="glyph"
|
||||
fill={COLORS.BLACK}
|
||||
d="M8.5,7.29791847 L6.12604076,4.92395924 C5.79409512,4.59201359 5.25590488,4.59201359 4.92395924,4.92395924 C4.59201359,5.25590488 4.59201359,5.79409512 4.92395924,6.12604076 L7.29791847,8.5 L4.92395924,10.8739592 C4.59201359,11.2059049 4.59201359,11.7440951 4.92395924,12.0760408 C5.25590488,12.4079864 5.79409512,12.4079864 6.12604076,12.0760408 L8.5,9.70208153 L10.8739592,12.0760408 C11.2059049,12.4079864 11.7440951,12.4079864 12.0760408,12.0760408 C12.4079864,11.7440951 12.4079864,11.2059049 12.0760408,10.8739592 L9.70208153,8.5 L12.0760408,6.12604076 C12.4079864,5.79409512 12.4079864,5.25590488 12.0760408,4.92395924 C11.7440951,4.59201359 11.2059049,4.59201359 10.8739592,4.92395924 L8.5,7.29791847 L8.5,7.29791847 Z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
function Billing(props) {
|
||||
const user = useAuth()
|
||||
|
||||
const [submit, { error, loading, data: success }] = useAsyncCallback(async e => {
|
||||
e.preventDefault()
|
||||
|
||||
const name = e.target.name.value.trim()
|
||||
|
||||
const res = await props.stripe.createToken({ name })
|
||||
|
||||
if (res.error) {
|
||||
throw res.error.message
|
||||
}
|
||||
|
||||
return {}
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="login">
|
||||
<div>
|
||||
<LoginButton />
|
||||
</div>
|
||||
<style jsx>
|
||||
{`
|
||||
.login {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="checkout">
|
||||
{success ? (
|
||||
<div className="column">
|
||||
<h4>Thank you for supporting Carbon!</h4>
|
||||
<p className="success">
|
||||
However, Carbon Diamond is not quite ready yet.
|
||||
<br />
|
||||
Your card has <u>not</u> been charged or saved today.
|
||||
<br />
|
||||
We greatly appreciate your support, and will contact you when these premium features
|
||||
launch!
|
||||
</p>
|
||||
<p className="success">
|
||||
— the Carbon Team{' '}
|
||||
<span role="img" aria-label="Black and yellow hearts">
|
||||
💛🖤
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="column">
|
||||
<h4>
|
||||
Upgrade to <span>Diamond</span>
|
||||
<br />
|
||||
<span className="tag">($5.00 / month)</span>
|
||||
</h4>
|
||||
<p>Please enter a credit or debit card:</p>
|
||||
<form onSubmit={submit}>
|
||||
<fieldset>
|
||||
<CardElement
|
||||
{...{
|
||||
iconStyle: 'solid',
|
||||
style: {
|
||||
base: {
|
||||
iconColor: COLORS.BLUE,
|
||||
color: COLORS.BLUE,
|
||||
fontWeight: 500,
|
||||
fontFamily: 'Roboto, Open Sans, Segoe UI, sans-serif',
|
||||
fontSize: '16px',
|
||||
fontSmoothing: 'antialiased',
|
||||
|
||||
':-webkit-autofill': {
|
||||
color: '#fce883'
|
||||
},
|
||||
'::placeholder': {
|
||||
color: 'rgba(255, 255, 255, 0.7)'
|
||||
}
|
||||
},
|
||||
invalid: {
|
||||
iconColor: COLORS.RED,
|
||||
color: COLORS.RED
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<hr />
|
||||
<Input placeholder="Cardholders's name..." name="name" required />
|
||||
</fieldset>
|
||||
<small>
|
||||
(By clicking subscribe, you are accepting the{' '}
|
||||
<a href="/terms">terms and conditions</a>)
|
||||
</small>
|
||||
<br />
|
||||
<Button
|
||||
display="inline-flex"
|
||||
border
|
||||
large
|
||||
padding="8px 16px"
|
||||
margin="8px 0 0"
|
||||
type="submit"
|
||||
color="rgba(255, 255, 255, 0.7)"
|
||||
>
|
||||
{loading ? 'Sending...' : 'Subscribe'}
|
||||
</Button>
|
||||
<div className={`error ${error ? 'visible' : ''}`} role="alert">
|
||||
{X}
|
||||
<span className="message">{error}</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
<style jsx>{`
|
||||
.checkout {
|
||||
position: relative;
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 4px;
|
||||
padding: 1rem 1.5rem;
|
||||
|
||||
color: white;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 8px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: block;
|
||||
font-weight: lighter;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 16px;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
margin: 0.5rem 0 1rem;
|
||||
background: ${COLORS.SECONDARY};
|
||||
}
|
||||
|
||||
fieldset {
|
||||
width: 100%;
|
||||
margin: 0 0 2.5rem;
|
||||
padding: 0.5rem 0.5rem 0.75rem;
|
||||
border: 1px solid ${COLORS.SECONDARY};
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
fieldset :global(input) {
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
color: ${COLORS.BLUE};
|
||||
}
|
||||
|
||||
fieldset :global(input::placeholder) {
|
||||
opacity: 1;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
fieldset :global(.StripeElement) {
|
||||
width: 100%;
|
||||
padding: 12px 16px 12px 0;
|
||||
}
|
||||
|
||||
form:valid :global(button) {
|
||||
color: ${COLORS.BLUE};
|
||||
box-shadow: inset 0px 0px 0px 1px ${COLORS.BLUE};
|
||||
}
|
||||
|
||||
.error {
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
top: +3px;
|
||||
opacity: 0;
|
||||
margin-left: 1rem;
|
||||
font-size: 12px;
|
||||
transform: translateY(20px);
|
||||
transition-property: opacity, transform;
|
||||
transition-duration: 0.35s;
|
||||
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
.error.visible {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.error svg {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.error .message {
|
||||
margin-left: 8px;
|
||||
font-size: inherit;
|
||||
color: ${COLORS.RED};
|
||||
}
|
||||
|
||||
.success {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const BillingWithStripe = injectStripe(Billing)
|
||||
|
||||
export default function() {
|
||||
const [stripe, setStripe] = React.useState(null)
|
||||
React.useEffect(() => {
|
||||
setStripe(window.Stripe(process.env.STRIPE_PUBLIC_KEY))
|
||||
}, [])
|
||||
return (
|
||||
<StripeProvider stripe={stripe}>
|
||||
<Elements>
|
||||
<BillingWithStripe />
|
||||
</Elements>
|
||||
</StripeProvider>
|
||||
)
|
||||
}
|
@ -0,0 +1,258 @@
|
||||
// Theirs
|
||||
import React from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
// Ours
|
||||
import Button from '../components/Button'
|
||||
import Page from '../components/Page'
|
||||
import MenuButton from '../components/MenuButton'
|
||||
import { MetaLinks } from '../components/Meta'
|
||||
import { useAuth } from '../components/AuthContext'
|
||||
|
||||
import { loginGitHub, logout } from '../lib/client'
|
||||
import { COLORS } from '../lib/constants'
|
||||
|
||||
const Billing = dynamic(() => import('../components/Billing'), {
|
||||
loading: () => <div style={{ minHeight: '360px' }} />
|
||||
})
|
||||
|
||||
function logoutThunk() {
|
||||
return logout
|
||||
}
|
||||
|
||||
const soon = <span title="Coming Soon">ⓘ</span>
|
||||
|
||||
function Plan({ selectBilling }) {
|
||||
const user = useAuth()
|
||||
|
||||
function handleSelectFree() {
|
||||
if (!user) {
|
||||
loginGitHub()
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelectUpgrade() {
|
||||
if (!user) {
|
||||
return loginGitHub()
|
||||
}
|
||||
|
||||
selectBilling()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="plan">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td />
|
||||
<td>
|
||||
<h3>Free</h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3 style={{ color: COLORS.BLUE }}>Diamond</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>PNG/SVG Exports</td>
|
||||
<td>✔</td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Full visual editor</td>
|
||||
<td>✔</td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Custom backgrounds</td>
|
||||
<td>✔</td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GitHub Gist support</td>
|
||||
<td>✔</td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Saved snippets</td>
|
||||
<td>1000</td>
|
||||
<td>∞</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Embed saved snippets</td>
|
||||
<td></td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API Access {soon}</td>
|
||||
<td></td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Saved custom themes/presets {soon}</td>
|
||||
<td></td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Twitter card unfurls {soon}</td>
|
||||
<td></td>
|
||||
<td>✔</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>FREE FOREVER</td>
|
||||
<td>$5.00 / month</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<Button
|
||||
large
|
||||
margin="0 auto"
|
||||
center
|
||||
border
|
||||
padding="4px 8px"
|
||||
color="white"
|
||||
disabled={user && user.plan === 'free'}
|
||||
onClick={handleSelectFree}
|
||||
>
|
||||
{user ? 'Current' : 'Get Started'}
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
large
|
||||
margin="0 auto"
|
||||
center
|
||||
border
|
||||
padding="4px 8px"
|
||||
color={COLORS.BLUE}
|
||||
disabled={user && user.plan === 'diamond'}
|
||||
onClick={handleSelectUpgrade}
|
||||
>
|
||||
Upgrade
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<style jsx>
|
||||
{`
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
td {
|
||||
font-size: 14px;
|
||||
padding: 0.5rem 0.5rem 0.5rem 1rem;
|
||||
}
|
||||
tr:nth-of-type(odd) {
|
||||
background: ${COLORS.HOVER};
|
||||
}
|
||||
thead tr {
|
||||
background: ${COLORS.BLACK};
|
||||
}
|
||||
|
||||
tr td:not(:last-of-type) {
|
||||
border-right: 1px solid white;
|
||||
}
|
||||
|
||||
tr td:not(:nth-child(1)) {
|
||||
text-align: center;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table :global(button) {
|
||||
text-transform: uppercase;
|
||||
font-size: 16px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Settings() {
|
||||
const [selected, select] = React.useState('Plan')
|
||||
const user = useAuth()
|
||||
|
||||
function selectMenu(name) {
|
||||
return () => select(name)
|
||||
}
|
||||
return (
|
||||
<div className="editor">
|
||||
<div className="settings-bottom">
|
||||
<div className="settings-menu">
|
||||
<MenuButton name="Plan" select={selectMenu} selected={selected}></MenuButton>
|
||||
<MenuButton name="Billing" select={selectMenu} selected={selected}></MenuButton>
|
||||
|
||||
{/* <MenuButton name="API Keys" select={selectMenu} selected={selected} /> */}
|
||||
<MenuButton name="Sign Out" select={logoutThunk} selected={selected} noArrows />
|
||||
</div>
|
||||
<div className="content">
|
||||
{selected === 'Plan' && <Plan selectBilling={selectMenu('Billing')} />}
|
||||
{selected === 'Billing' && <Billing />}
|
||||
</div>
|
||||
</div>
|
||||
{user && <img className="avatar" src={user.photoURL} alt={user.displayName} />}
|
||||
<style jsx>
|
||||
{`
|
||||
.editor {
|
||||
position: relative;
|
||||
background: ${COLORS.BLACK};
|
||||
border: 3px solid ${COLORS.SECONDARY};
|
||||
border-radius: 8px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.settings-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.settings-bottom {
|
||||
display: flex;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.settings-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 96px;
|
||||
background-color: ${COLORS.DARK_GRAY};
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 580px;
|
||||
border-left: 3px solid ${COLORS.SECONDARY};
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: ${COLORS.BLACK};
|
||||
border: 3px solid ${COLORS.SECONDARY};
|
||||
top: -40px;
|
||||
right: -40px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SettingsPage() {
|
||||
return (
|
||||
<Page>
|
||||
<MetaLinks />
|
||||
<Settings />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default SettingsPage
|
@ -0,0 +1,213 @@
|
||||
// Theirs
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import Router from 'next/router'
|
||||
import formatDistanceToNow from 'date-fns/formatDistanceToNow'
|
||||
import { useAsyncCallback } from '@dawnlabs/tacklebox'
|
||||
|
||||
import Button from '../components/Button'
|
||||
import LoginButton from '../components/LoginButton'
|
||||
import { useAuth } from '../components/AuthContext'
|
||||
import { useAPI } from '../components/ApiContext'
|
||||
|
||||
import { MetaLinks } from '../components/Meta'
|
||||
import Carbon from '../components/Carbon'
|
||||
|
||||
import { COLORS, DEFAULT_SETTINGS } from '../lib/constants'
|
||||
|
||||
// Ours
|
||||
import Page from '../components/Page'
|
||||
|
||||
function correctTimestamp(n) {
|
||||
if (n < 9e12) {
|
||||
return n * 1000
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
function Snippet(props) {
|
||||
const config = { ...DEFAULT_SETTINGS, ...props, fontSize: '2px', windowControls: false }
|
||||
|
||||
return (
|
||||
<Link prefetch={false} href={`/${props.id}`}>
|
||||
<a href={`/${props.id}`}>
|
||||
<div className="snippet">
|
||||
<div className="column">
|
||||
<div className="carbon-container">
|
||||
<Carbon key={config.language} readOnly={true} config={config} loading={props.loading}>
|
||||
{props.code}
|
||||
</Carbon>
|
||||
</div>
|
||||
<div className="id">{props.title || props.id}</div>
|
||||
<div className="meta">
|
||||
Edited {formatDistanceToNow(correctTimestamp(props.updatedAt), { addSuffix: true })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style jsx>
|
||||
{`
|
||||
.snippet {
|
||||
position: relative;
|
||||
width: 266px;
|
||||
height: 266px;
|
||||
border: 1px solid ${COLORS.SECONDARY};
|
||||
border-radius: 3px;
|
||||
cursor: pointer !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
.snippet:hover {
|
||||
background: ${COLORS.HOVER};
|
||||
}
|
||||
.snippet:hover:after {
|
||||
position: absolute;
|
||||
content: '↗';
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: ${COLORS.HOVER};
|
||||
opacity: 0.8;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
font-size: 48px;
|
||||
z-index: 999;
|
||||
}
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.id {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.125rem;
|
||||
border-radius: 3px;
|
||||
background: ${COLORS.SECONDARY};
|
||||
color: ${COLORS.BLACK};
|
||||
font-size: 10px;
|
||||
padding: 0.125rem;
|
||||
max-width: 80%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
z-index: 200;
|
||||
}
|
||||
.carbon-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
color: ${COLORS.SECONDARY};
|
||||
width: 266px;
|
||||
}
|
||||
.carbon-container :global(.CodeMirror__container .CodeMirror span) {
|
||||
font-size: 2px !important;
|
||||
}
|
||||
.meta {
|
||||
background: ${COLORS.SECONDARY};
|
||||
color: ${COLORS.DARK_GRAY};
|
||||
width: 100%;
|
||||
font-size: 10px;
|
||||
padding: 0.24rem;
|
||||
bottom: 0px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function ActionButton(props) {
|
||||
return (
|
||||
<Button
|
||||
border
|
||||
center
|
||||
margin="0.5rem"
|
||||
flex="0 0 266px"
|
||||
color={COLORS.GRAY}
|
||||
style={{ minHeight: 266 }}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function useOnMount() {
|
||||
const [mounted, mount] = React.useState(false)
|
||||
React.useEffect(() => {
|
||||
mount(true)
|
||||
}, [])
|
||||
|
||||
return mounted
|
||||
}
|
||||
|
||||
function SnippetsPage() {
|
||||
const user = useAuth()
|
||||
const api = useAPI()
|
||||
|
||||
const [snippets, setSnippets] = React.useState([])
|
||||
const [page, setPage] = React.useState(0)
|
||||
|
||||
const mounted = useOnMount()
|
||||
|
||||
const [loadMore, { loading, data: previousRes }] = useAsyncCallback(api.snippet.list)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (user) {
|
||||
const authorization = user.ra
|
||||
loadMore(page, { authorization }).then(newSnippets =>
|
||||
setSnippets(curr => curr.concat(newSnippets))
|
||||
)
|
||||
}
|
||||
}, [loadMore, page, user])
|
||||
|
||||
if (!user) {
|
||||
return <LoginButton />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
{snippets.map(snippet => (
|
||||
<Snippet key={snippet.id} {...snippet} loading={!mounted} />
|
||||
))}
|
||||
{snippets.length && previousRes && previousRes.length < 10 ? null : (
|
||||
<ActionButton
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
if (snippets.length) return setPage(p => p + 1)
|
||||
|
||||
Router.push('/')
|
||||
}}
|
||||
>
|
||||
<h4>{loading ? 'Loading...' : !snippets.length ? 'Create snippet +' : 'Load more +'}</h4>
|
||||
</ActionButton>
|
||||
)}
|
||||
<style jsx>
|
||||
{`
|
||||
.container {
|
||||
max-width: 872px;
|
||||
min-width: ${266 + 32 + 8}px;
|
||||
border: 3px solid ${COLORS.SECONDARY};
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.container :global(.snippet) {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default () => (
|
||||
<Page enableHeroText={true}>
|
||||
<MetaLinks />
|
||||
<SnippetsPage />
|
||||
</Page>
|
||||
)
|
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 882 B |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="white" d="M256 256c52.805 0 96-43.201 96-96s-43.195-96-96-96-96 43.201-96 96 43.195 96 96 96zm0 48c-63.598 0-192 32.402-192 96v48h384v-48c0-63.598-128.402-96-192-96z"/></svg>
|
After Width: | Height: | Size: 248 B |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="white" d="M190.4 354.1L91.9 256l98.4-98.1-30-29.9L32 256l128.4 128 30-29.9zm131.2 0L420 256l-98.4-98.1 30-29.9L480 256 351.6 384l-30-29.9z"/><path fill="white" d="M155.6 276h40v-40h-40v40zm200.8-40h-40v40h40v-40zM236 276h40v-40h-40v40z"/></svg>
|
After Width: | Height: | Size: 319 B |
Loading…
Reference in New Issue