@@ -1,5 +1,6 @@ | |||
{ | |||
"name": "@tesseract-design/react-common", | |||
"title": "React Common", | |||
"version": "0.2.3", | |||
"description": "Common front-end components for Web using the Tesseract design system, written in React.", | |||
"directories": { | |||
@@ -16,6 +16,8 @@ | |||
"next": "10.0.1", | |||
"next-mdx-frontmatter": "^0.0.3", | |||
"pascal-case": "^3.1.1", | |||
"prism-react-renderer": "^1.1.1", | |||
"prismjs": "^1.22.0", | |||
"react-docgen-typescript": "^1.20.5", | |||
"react-live": "^2.2.3", | |||
"remark-parse": "^9.0.0", | |||
@@ -55,4 +55,5 @@ pre { | |||
overflow: auto; | |||
margin: 0 -1rem; | |||
padding: 0 1rem; | |||
line-height: 1.2; | |||
} |
@@ -0,0 +1,71 @@ | |||
import * as React from 'react' | |||
import styled from 'styled-components' | |||
import Highlight, { defaultProps } from 'prism-react-renderer' | |||
import PrismTheme from '../../utilities/prism-themes/dark' | |||
const Base = styled('figure')({ | |||
margin: 0, | |||
}) | |||
const Title = styled('figcaption')({ | |||
textTransform: 'uppercase', | |||
fontWeight: 'bolder', | |||
fontSize: '0.75rem', | |||
}) | |||
const CodeBlock = ({ children }) => { | |||
const { props, } = children | |||
const { children: code, className, metastring } = props | |||
const language = typeof className as string === 'string' ? className.split('-')[1] : 'plain' | |||
const meta = (typeof metastring as string === 'string' && metastring.length > 0) | |||
? (metastring as string) | |||
.split(' ') | |||
.reduce( | |||
(m, s) => { | |||
const [key, value = ''] = s.split('=') | |||
if (value.trim().length > 0) { | |||
return { | |||
...m, | |||
[key.trim()]: value.trim(), | |||
} | |||
} | |||
return { | |||
...m, | |||
[key.trim()]: true, | |||
} | |||
}, | |||
{} | |||
) | |||
: {} | |||
return ( | |||
<Base> | |||
{ | |||
meta['title'] && ( | |||
<Title> | |||
{meta['title']} | |||
</Title> | |||
) | |||
} | |||
<Highlight | |||
{...defaultProps} | |||
language={language} | |||
code={(code as string).trim()} | |||
theme={PrismTheme} | |||
> | |||
{({ style, tokens, getLineProps, getTokenProps }) => ( | |||
<pre style={style}> | |||
{tokens.map((line, i) => ( | |||
<div {...getLineProps({ line, key: i })}> | |||
{line.map((token, key) => ( | |||
<span {...getTokenProps({ token, key })} /> | |||
))} | |||
</div> | |||
))} | |||
</pre> | |||
)} | |||
</Highlight> | |||
</Base> | |||
) | |||
} | |||
export default CodeBlock |
@@ -5,6 +5,7 @@ import unified from 'unified' | |||
import parse from 'remark-parse' | |||
import remark2react from 'remark-react' | |||
import docgen from '../../docgen.json' | |||
import pkg from '../../../../../package.json' | |||
const propTypes = { | |||
of: PropTypes.string, | |||
@@ -23,8 +24,9 @@ const Header: React.FC<Props> = ({ of: ofAttr }) => { | |||
<React.Fragment> | |||
<Head> | |||
<title> | |||
{docs.displayName} | React Common | |||
{docs.displayName} | {pkg['title'] || pkg.name} | |||
</title> | |||
<meta name="description" content={docs.description} key="description" /> | |||
</Head> | |||
<h1>{docs.displayName}</h1> | |||
<p> | |||
@@ -18,6 +18,9 @@ const Container = styled('span')({ | |||
const HeaderContainer = styled(Container)({ | |||
marginTop: '2rem', | |||
fontSize: '0.75rem', | |||
fontWeight: 'bolder', | |||
textTransform: 'uppercase', | |||
}) | |||
const propTypes = { | |||
@@ -3,6 +3,7 @@ import * as PropTypes from 'prop-types' | |||
import styled from 'styled-components' | |||
import { Icon } from '../../../../react-common/src' | |||
import MenuGraphics, { propTypes as menuGraphicsPropTypes } from '../MenuGraphics/MenuGraphics' | |||
import { useRouter } from 'next/router' | |||
const Link = styled('a')({ | |||
display: 'block', | |||
@@ -41,6 +42,15 @@ const Link = styled('a')({ | |||
}, | |||
}) | |||
const ActiveLink = styled(Link)({ | |||
'::before': { | |||
opacity: 0.25, | |||
}, | |||
'::after': { | |||
opacity: 0.5, | |||
}, | |||
}) | |||
const LinkText = styled('span')({ | |||
alignSelf: 'center', | |||
display: 'block', | |||
@@ -102,55 +112,60 @@ const NavLink = React.forwardRef<HTMLAnchorElement, Props>(( | |||
onClick, | |||
}, | |||
ref | |||
) => ( | |||
<Link | |||
ref={ref} | |||
href={href} | |||
onClick={onClick as (...args: any[]) => any} | |||
> | |||
{ | |||
// @ts-ignore | |||
<EnclosingComponent> | |||
{ | |||
graphics as object | |||
&& ( | |||
<MenuGraphicsContainer> | |||
{ | |||
// @ts-ignore | |||
<MenuGraphics | |||
{...graphics} | |||
/> | |||
} | |||
</MenuGraphicsContainer> | |||
) | |||
} | |||
<LinkText> | |||
<LinkTitle> | |||
{title} | |||
</LinkTitle> | |||
) => { | |||
const router = useRouter() | |||
const active = router.basePath + router.route === href | |||
const Component = active ? ActiveLink : Link | |||
return ( | |||
<Component | |||
ref={ref} | |||
href={href} | |||
onClick={onClick as (...args: any[]) => any} | |||
> | |||
{ | |||
// @ts-ignore | |||
<EnclosingComponent> | |||
{ | |||
subtitle as string | |||
graphics as object | |||
&& ( | |||
<LinkSubtitle> | |||
{subtitle} | |||
</LinkSubtitle> | |||
<MenuGraphicsContainer> | |||
{ | |||
// @ts-ignore | |||
<MenuGraphics | |||
{...graphics} | |||
/> | |||
} | |||
</MenuGraphicsContainer> | |||
) | |||
} | |||
</LinkText> | |||
{ | |||
indicator as string | |||
&& ( | |||
<IndicatorContainer> | |||
<Icon | |||
name={indicator!} | |||
/> | |||
</IndicatorContainer> | |||
) | |||
} | |||
</EnclosingComponent> | |||
} | |||
</Link> | |||
)) | |||
<LinkText> | |||
<LinkTitle> | |||
{title} | |||
</LinkTitle> | |||
{ | |||
subtitle as string | |||
&& ( | |||
<LinkSubtitle> | |||
{subtitle} | |||
</LinkSubtitle> | |||
) | |||
} | |||
</LinkText> | |||
{ | |||
indicator as string | |||
&& ( | |||
<IndicatorContainer> | |||
<Icon | |||
name={indicator!} | |||
/> | |||
</IndicatorContainer> | |||
) | |||
} | |||
</EnclosingComponent> | |||
} | |||
</Component> | |||
) | |||
}) | |||
NavLink.propTypes = propTypes | |||
@@ -1,5 +1,6 @@ | |||
import * as React from 'react' | |||
import * as PropTypes from 'prop-types' | |||
import PrismTheme from '../../utilities/prism-themes/dark' | |||
import { | |||
LiveProvider, | |||
LiveEditor, | |||
@@ -8,13 +9,42 @@ import { | |||
import styled from 'styled-components' | |||
const Figure = styled('figure')({ | |||
margin: 0, | |||
margin: '0 -1rem', | |||
padding: '1rem', | |||
boxSizing: 'border-box', | |||
position: 'relative', | |||
'::before': { | |||
position: 'absolute', | |||
content: "''", | |||
zIndex: -1, | |||
backgroundColor: 'black', | |||
opacity: 0.125, | |||
top: 0, | |||
left: 0, | |||
width: '100%', | |||
height: '100%', | |||
}, | |||
}) | |||
const Caption = styled('figcaption')({ | |||
fontSize: '0.75rem', | |||
fontWeight: 'bolder', | |||
textTransform: 'uppercase', | |||
marginBottom: '1rem', | |||
marginTop: '-0.5rem', | |||
}) | |||
const StyledLiveEditor = styled(LiveEditor)({ | |||
lineHeight: 1.125, | |||
fontFamily: 'var(--font-family-monospace), monospace !important', | |||
marginTop: '1rem', | |||
'textarea': { | |||
padding: '0 !important', | |||
outline: 0, | |||
}, | |||
'pre': { | |||
padding: '0 !important', | |||
}, | |||
}) | |||
const propTypes = { | |||
@@ -42,9 +72,17 @@ const Playground: React.FC<Props> = ({ | |||
{ | |||
label as string | |||
&& ( | |||
<figcaption> | |||
{label} | |||
</figcaption> | |||
<Caption> | |||
Playground - {label} | |||
</Caption> | |||
) | |||
} | |||
{ | |||
!label | |||
&& ( | |||
<Caption> | |||
Playground | |||
</Caption> | |||
) | |||
} | |||
<div> | |||
@@ -52,7 +90,9 @@ const Playground: React.FC<Props> = ({ | |||
...components, | |||
}}> | |||
<LivePreview /> | |||
<StyledLiveEditor /> | |||
<StyledLiveEditor | |||
theme={PrismTheme} | |||
/> | |||
</LiveProvider> | |||
</div> | |||
</Figure> | |||
@@ -1,6 +1,7 @@ | |||
import * as React from 'react' | |||
import * as PropTypes from 'prop-types' | |||
import styled from 'styled-components' | |||
import PrismTheme from '../../utilities/prism-themes/dark' | |||
import docgen from '../../docgen.json' | |||
const Base = styled('table')({ | |||
@@ -114,6 +115,150 @@ type Docs = { | |||
} | |||
} | |||
const getPropName = (name, def) => def.required ? name : name + '?' | |||
const getPropType = def => { | |||
switch (def.type.name) { | |||
case '(...args: any[]) => any': | |||
return 'Function' | |||
case 'enum': | |||
return def.type.value.map(v => v.value).join(' | ') | |||
default: | |||
break | |||
} | |||
return def.type.name | |||
} | |||
const getPropDefaultValue = def => { | |||
return def.defaultValue | |||
? JSON.stringify(def.defaultValue.value) | |||
: undefined | |||
} | |||
const getPrismStyle = style => PrismTheme.styles.find(s => s.types.includes(style)) || PrismTheme.plain | |||
const getPropNameHtml = (propName, propType) => { | |||
const variable = getPrismStyle('variable') | |||
const operator = getPrismStyle('operator') | |||
const fn = getPrismStyle('function') | |||
const isFunction = propType === 'Function' | |||
const trueStyle = isFunction ? fn : variable | |||
return ( | |||
propName.endsWith('?') | |||
? ( | |||
<React.Fragment> | |||
<span | |||
style={trueStyle.style} | |||
> | |||
{propName.slice(0, -1)} | |||
</span> | |||
<span | |||
style={operator.style} | |||
> | |||
{propName.slice(-1)} | |||
</span> | |||
</React.Fragment> | |||
) | |||
: ( | |||
<span | |||
style={trueStyle.style} | |||
> | |||
{propName} | |||
</span> | |||
) | |||
) | |||
} | |||
const getPropTypeHtml = propType => { | |||
if (!propType) { | |||
return undefined | |||
} | |||
const punctuation = getPrismStyle('punctuation') | |||
const string = getPrismStyle('string') | |||
const operator = getPrismStyle('operator') | |||
const keyword = getPrismStyle('keyword') | |||
const boolean = getPrismStyle('boolean') | |||
const number = getPrismStyle('number') | |||
const type = getPrismStyle('type') | |||
const tokens = propType.split('|') | |||
return tokens.reduce( | |||
(tt, t, i) => { | |||
let currentToken | |||
if (t.trim().startsWith('"') && t.trim().endsWith('"')) { | |||
currentToken = ( | |||
<React.Fragment> | |||
<span | |||
style={punctuation.style} | |||
> | |||
{t.slice(0, t.indexOf('"') + 1)} | |||
</span> | |||
<span | |||
style={string.style} | |||
> | |||
{t.slice(t.indexOf('"') + 1, t.lastIndexOf('"'))} | |||
</span> | |||
<span | |||
style={punctuation.style} | |||
> | |||
{t.slice(t.lastIndexOf('"'))} | |||
</span> | |||
</React.Fragment> | |||
) | |||
} else if ('any string boolean number symbol unknown object bigint'.split(' ').includes(t)) { | |||
currentToken = ( | |||
<span | |||
style={keyword.style} | |||
> | |||
{t} | |||
</span> | |||
) | |||
} else if ('null'.split(' ').includes(t)) { | |||
currentToken = ( | |||
<span | |||
style={keyword.style} | |||
> | |||
{t} | |||
</span> | |||
) | |||
} else if ('true false'.split(' ').includes(t)) { | |||
currentToken = ( | |||
<span | |||
style={boolean.style} | |||
> | |||
{t} | |||
</span> | |||
) | |||
} else if (!isNaN(Number(t))) { | |||
currentToken = ( | |||
<span | |||
style={number.style} | |||
> | |||
{t} | |||
</span> | |||
) | |||
} else { | |||
currentToken = ( | |||
<span | |||
style={type.style} | |||
> | |||
{t} | |||
</span> | |||
) | |||
} | |||
return [ | |||
...tt, | |||
i > 0 && <span | |||
style={operator.style} | |||
> | |||
| | |||
</span>, | |||
currentToken, | |||
] | |||
}, | |||
[] | |||
) | |||
} | |||
const Props: React.FC<Props> = ({ of: ofAttr }) => { | |||
const docs = (docgen as unknown as Docs[]).find(d => d.displayName === ofAttr) | |||
@@ -147,50 +292,37 @@ const Props: React.FC<Props> = ({ of: ofAttr }) => { | |||
</HeaderCellGroup> | |||
<BodyCellGroup> | |||
{ | |||
Object.entries((docs as Docs).props).map(([name, def]) => ( | |||
<HeaderRow> | |||
<MainBodyCell> | |||
<Code> | |||
{def.required ? name : name + '?'} | |||
</Code> | |||
</MainBodyCell> | |||
<BodyCell | |||
data-column-name="Type" | |||
> | |||
{ | |||
def.type.name === 'enum' | |||
&& ( | |||
<Code> | |||
{def.type.value.map(v => v.value).join(' | ')} | |||
</Code> | |||
) | |||
} | |||
{ | |||
def.type.name !== 'enum' | |||
&& ( | |||
<Code> | |||
{def.type.name} | |||
</Code> | |||
) | |||
} | |||
</BodyCell> | |||
<BodyCell | |||
data-column-name={def.defaultValue ? 'Default' : undefined } | |||
> | |||
{ | |||
def.defaultValue | |||
&& ( | |||
<Code> | |||
{JSON.stringify(def.defaultValue.value)} | |||
</Code> | |||
) | |||
} | |||
</BodyCell> | |||
<BodyCell> | |||
{def.description} | |||
</BodyCell> | |||
</HeaderRow> | |||
)) | |||
Object.entries((docs as Docs).props).map(([name, prop]) => { | |||
const propName = getPropName(name, prop) | |||
const propDefaultValue = getPropDefaultValue(prop) | |||
const propType = getPropType(prop) | |||
return ( | |||
<HeaderRow> | |||
<MainBodyCell> | |||
<Code> | |||
{getPropNameHtml(propName, propType)} | |||
</Code> | |||
</MainBodyCell> | |||
<BodyCell | |||
data-column-name="Type" | |||
> | |||
<Code> | |||
{getPropTypeHtml(propType)} | |||
</Code> | |||
</BodyCell> | |||
<BodyCell | |||
data-column-name={propDefaultValue ? 'Default' : undefined} | |||
> | |||
<Code> | |||
{getPropTypeHtml(propDefaultValue)} | |||
</Code> | |||
</BodyCell> | |||
<BodyCell> | |||
{prop.description} | |||
</BodyCell> | |||
</HeaderRow> | |||
) | |||
}) | |||
} | |||
</BodyCellGroup> | |||
</Base> | |||
@@ -6,6 +6,7 @@ import styled from 'styled-components' | |||
import Link from 'next/link' | |||
import Nav from '../Nav/Nav' | |||
import { MouseEventHandler } from 'react' | |||
import ThemeToggle from '../ThemeToggle/ThemeToggle' | |||
const StyledLink = styled('a')({ | |||
display: 'block', | |||
@@ -82,21 +83,6 @@ const Actions = styled('div')({ | |||
alignItems: 'center', | |||
}) | |||
const ToggleWrapper = styled('label')({ | |||
cursor: 'pointer', | |||
color: 'var(--color-accent)', | |||
display: 'inline-block', | |||
}) | |||
const ToggleIcon = styled('span')({ | |||
}) | |||
const ToggleInput = styled('input')({ | |||
position: 'absolute', | |||
left: -999999, | |||
}) | |||
const NavWrapper = styled('nav')({ | |||
'--size-link': '3rem', | |||
marginBottom: '4rem', | |||
@@ -122,82 +108,39 @@ type Props = PropTypes.InferProps<typeof propTypes> | |||
const Sidebar: React.FC<Props> = ({ | |||
data, | |||
brand: BrandRaw, | |||
initialTheme = 'Dark', | |||
initialTheme, | |||
}) => { | |||
const [theme, setTheme] = React.useState(initialTheme) | |||
const [sidebar, setSidebar] = React.useState(false) | |||
const navRef = React.useRef<HTMLDivElement>(null) | |||
const Brand = BrandRaw! | |||
const toggleDarkMode = (b: string) => () => { | |||
setTheme(b) | |||
} | |||
const toggleSidebar = (b: boolean): MouseEventHandler => (e) => { | |||
e.preventDefault() | |||
setSidebar(b) | |||
} | |||
React.useEffect(() => { | |||
let storageTheme = window.localStorage.getItem('tesseract-theme') | |||
|| ( | |||
window.matchMedia('(prefers-color-scheme: dark)').matches | |||
? 'Dark' | |||
: 'Light' | |||
) | |||
window.localStorage.setItem('tesseract-theme', storageTheme) | |||
setTimeout(() => { | |||
setTheme(storageTheme) | |||
}) | |||
}, []) | |||
React.useEffect(() => { | |||
const eventListener = (e: MouseEvent) => { | |||
let currentElement = e.target as HTMLElement | |||
while (currentElement !== e.currentTarget) { | |||
if (currentElement.tagName === 'A') { | |||
setSidebar(false) | |||
break | |||
} | |||
const { parentElement } = currentElement | |||
if (parentElement as HTMLElement === null) { | |||
break | |||
} | |||
currentElement = parentElement as HTMLElement | |||
const handleSidebarClose = (e: MouseEvent) => { | |||
let currentElement = e.target as HTMLElement | |||
while (currentElement !== e.currentTarget) { | |||
if (currentElement.tagName === 'A') { | |||
setSidebar(false) | |||
break | |||
} | |||
const { parentElement } = currentElement | |||
if (parentElement as HTMLElement === null) { | |||
break | |||
} | |||
currentElement = parentElement as HTMLElement | |||
} | |||
} | |||
if (navRef.current === null) { | |||
return | |||
} | |||
navRef.current.addEventListener('click', eventListener, { capture: true }) | |||
React.useEffect(() => { | |||
navRef.current.addEventListener('click', handleSidebarClose, { capture: true }) | |||
return () => { | |||
if (navRef.current === null) { | |||
return | |||
} | |||
navRef.current.removeEventListener('click', eventListener, { capture: true }) | |||
navRef.current.removeEventListener('click', handleSidebarClose, { capture: true }) | |||
} | |||
}, []) | |||
React.useEffect(() => { | |||
window.localStorage.setItem('tesseract-theme', theme as string) | |||
}, [theme]) | |||
React.useEffect(() => { | |||
const stylesheets = Array.from(window.document.querySelectorAll('link[title]')) as HTMLLinkElement[] | |||
stylesheets.forEach(s => { | |||
const enabled = s.title === theme | |||
s.setAttribute('rel', enabled ? 'stylesheet' : 'alternate stylesheet') | |||
if (enabled) { | |||
s.removeAttribute('disabled') | |||
} else { | |||
s.setAttribute('disabled', 'disabled') | |||
} | |||
}) | |||
}, [theme]) | |||
return ( | |||
<React.Fragment> | |||
<SidebarToggle | |||
@@ -237,33 +180,9 @@ const Sidebar: React.FC<Props> = ({ | |||
</Link> | |||
<Container> | |||
<Actions> | |||
<ToggleWrapper> | |||
<ToggleInput | |||
type="checkbox" | |||
defaultChecked={theme === 'Dark'} | |||
onChange={toggleDarkMode(theme === 'Dark' ? 'Light' : 'Dark')} | |||
/> | |||
<ToggleIcon> | |||
{ | |||
theme === 'Dark' | |||
&& ( | |||
<Icon | |||
label="Set Light Mode" | |||
name="moon" | |||
/> | |||
) | |||
} | |||
{ | |||
theme === 'Light' | |||
&& ( | |||
<Icon | |||
label="Set Dark Mode" | |||
name="sun" | |||
/> | |||
) | |||
} | |||
</ToggleIcon> | |||
</ToggleWrapper> | |||
<ThemeToggle | |||
initialTheme={initialTheme} | |||
/> | |||
<RepoLink | |||
href={pkg.repository} | |||
target="_blank" | |||
@@ -0,0 +1,96 @@ | |||
import * as React from 'react' | |||
import styled from 'styled-components' | |||
import { Icon } from '../../../../react-common/src' | |||
const ToggleWrapper = styled('label')({ | |||
cursor: 'pointer', | |||
color: 'var(--color-accent)', | |||
display: 'inline-block', | |||
}) | |||
const ToggleInput = styled('input')({ | |||
position: 'absolute', | |||
left: -999999, | |||
}) | |||
const getAutoDetectTheme = () => ( | |||
window.matchMedia('(prefers-color-scheme: dark)').matches | |||
? 'Dark' | |||
: 'Light' | |||
) | |||
const getTheme = () => window.localStorage.getItem('tesseract-theme') || getAutoDetectTheme() | |||
const applyStyles = (theme) => { | |||
const stylesheets = Array.from(window.document.querySelectorAll('link[title]')) as HTMLLinkElement[] | |||
stylesheets.forEach(s => { | |||
const enabled = s.title === theme | |||
if (enabled) { | |||
s.removeAttribute('disabled') | |||
} else { | |||
s.setAttribute('disabled', 'disabled') | |||
} | |||
}) | |||
} | |||
const handleInitializeTheme = (initialTheme: string) => () => { | |||
applyStyles(getTheme() || initialTheme) | |||
} | |||
const ThemeToggle = ({ | |||
initialTheme, | |||
}) => { | |||
const [theme, setTheme] = React.useState(initialTheme!) | |||
const handleSetTheme = (t: string) => () => { | |||
setTheme(t) | |||
} | |||
React.useEffect(() => { | |||
window.localStorage.setItem('tesseract-theme', theme as string) | |||
}, [theme]) | |||
React.useEffect(() => { | |||
applyStyles(theme) | |||
}, [theme]) | |||
React.useEffect(() => { | |||
const handler = handleInitializeTheme(initialTheme) | |||
window.addEventListener('load', handler) | |||
return () => { | |||
window.removeEventListener('load', handler) | |||
} | |||
}, [initialTheme]) | |||
return ( | |||
<ToggleWrapper> | |||
<ToggleInput | |||
type="checkbox" | |||
defaultChecked={theme === 'Dark'} | |||
onChange={handleSetTheme(theme === 'Dark' ? 'Light' : 'Dark')} | |||
/> | |||
<span> | |||
{ | |||
theme === 'Dark' | |||
&& ( | |||
<Icon | |||
label="Set Light Mode" | |||
name="moon" | |||
/> | |||
) | |||
} | |||
{ | |||
theme === 'Light' | |||
&& ( | |||
<Icon | |||
label="Set Dark Mode" | |||
name="sun" | |||
/> | |||
) | |||
} | |||
</span> | |||
</ToggleWrapper> | |||
) | |||
} | |||
export default ThemeToggle |
@@ -251,7 +251,7 @@ | |||
"defaultValue": { | |||
"value": null | |||
}, | |||
"description": "Describe of what the component represents.", | |||
"description": "Description of what the component represents.", | |||
"name": "label", | |||
"required": false, | |||
"type": { | |||
@@ -1,10 +1,12 @@ | |||
import * as React from 'react' | |||
import { MDXProvider } from '@mdx-js/react' | |||
import Head from 'next/head' | |||
import styled from 'styled-components' | |||
import sidebar from '../sidebar.json' | |||
import brand from '../../brand' | |||
import Sidebar from '../components/Sidebar/Sidebar' | |||
import '../../public/global.css' | |||
import '../../public/theme/dark.css' | |||
import pkg from '../../../../package.json' | |||
import CodeBlock from '../components/CodeBlock/CodeBlock' | |||
const Container = styled('div')({ | |||
maxWidth: 720, | |||
@@ -27,15 +29,26 @@ const App: React.FC<AppProps> = ({ | |||
pageProps, | |||
}) => ( | |||
<React.Fragment> | |||
<Head> | |||
<title>{pkg['title'] || pkg.name}</title> | |||
<meta name="description" content={pkg.description} key="description" /> | |||
</Head> | |||
<Sidebar | |||
brand={brand} | |||
data={sidebar} | |||
initialTheme="Dark" | |||
/> | |||
<Main> | |||
<Container> | |||
<Component | |||
{...pageProps} | |||
/> | |||
<MDXProvider | |||
components={{ | |||
pre: CodeBlock, | |||
}} | |||
> | |||
<Component | |||
{...pageProps} | |||
/> | |||
</MDXProvider> | |||
</Container> | |||
</Main> | |||
</React.Fragment> | |||
@@ -1,12 +1,14 @@ | |||
import pkg from '../../../../package.json' | |||
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document' | |||
import { ServerStyleSheet } from 'styled-components' | |||
import pkg from '../../../../package.json' | |||
import config from '../../next.config' | |||
const publicUrl = process.env.NODE_ENV === 'production' ? pkg.homepage : config.basePath | |||
export default class MyDocument extends Document { | |||
static async getInitialProps(ctx: DocumentContext) { | |||
const sheet = new ServerStyleSheet() | |||
const originalRenderPage = ctx.renderPage | |||
const publicUrl = process.env.NODE_ENV === 'production' ? pkg.homepage : '' | |||
try { | |||
ctx.renderPage = () => | |||
@@ -38,7 +38,7 @@ act as guide to the user on how long the expected input values are. | |||
components={{ TextInput }} | |||
code={` | |||
<form> | |||
I am <span style={{ display: 'inline-block', width: '16rem', verticalAlign: 'bottom', }}><TextInput label="Full name" hint="given and family name" /></span> and I live in <span style={{ display: 'inline-block', width: '24rem', verticalAlign: 'bottom', }}><TextInput label="Address" hint="city, state and country" /></span>. | |||
I am <span style={{ display: 'inline-block', width: '12rem', verticalAlign: 'bottom', }}><TextInput label="Full name" hint="given and family name" /></span> and I live in <span style={{ display: 'inline-block', width: '16rem', verticalAlign: 'bottom', }}><TextInput label="Address" hint="city, state and country" /></span>. | |||
</form> | |||
`} | |||
/> | |||
@@ -50,6 +50,7 @@ element specified as `grid`. This is to be able to add complementing content to | |||
some content that is best displayed outside the component instead of putting in the `hint` prop. | |||
<Playground | |||
label="Example shipping address form" | |||
components={{ TextInput }} | |||
code={` | |||
<form | |||
@@ -51,8 +51,7 @@ The base font family. Used for body text. | |||
You should have a global theme rule applied to `:root`. Below is an example of using a global theme to apply styles: | |||
`theme.css` | |||
```css | |||
```css title=theme.css | |||
:root { | |||
/* This will be our reference background color */ | |||
--color-negative: #eee; | |||
@@ -0,0 +1,141 @@ | |||
import { PrismTheme } from 'prism-react-renderer' | |||
const theme: PrismTheme = { | |||
plain: { | |||
backgroundColor: "transparent", | |||
color: "#ffffff" | |||
}, | |||
styles: [ | |||
{ | |||
types: ["comment", "prolog", "doctype", "cdata"], | |||
style: { | |||
// color: "#6c6783" | |||
opacity: 0.3, | |||
fontStyle: 'italic', | |||
} | |||
}, | |||
{ | |||
types: ["namespace"], | |||
style: { | |||
opacity: 0.7 | |||
} | |||
}, | |||
{ | |||
types: ["number", "boolean", "hexcode"], | |||
style: { | |||
color: "#74f95e", | |||
fontWeight: 'bold', | |||
} | |||
}, | |||
{ | |||
types: ["tag", "operator", "keyword", "atrule", "selector"], | |||
style: { | |||
color: "#ff4389", | |||
fontWeight: 'bold', | |||
} | |||
}, | |||
{ | |||
types: ["type", "class"], | |||
style: { | |||
color: "#5097D2", | |||
fontWeight: 'bold', | |||
} | |||
}, | |||
{ | |||
types: ["punctuation"], | |||
style: { | |||
color: "#ffffff" | |||
} | |||
}, | |||
{ | |||
types: ["function"], | |||
style: { | |||
color: "#67c252", | |||
fontStyle: 'italic', | |||
} | |||
}, | |||
{ | |||
types: ["parameter"], | |||
style: { | |||
color: "#915ec2" | |||
} | |||
}, | |||
{ | |||
types: ["property"], | |||
style: { | |||
color: "#ffa1c9" | |||
} | |||
}, | |||
{ | |||
types: ["tag-id", "atrule-id"], | |||
style: { | |||
color: "#eeebff" | |||
} | |||
}, | |||
{ | |||
types: ["attr-name"], | |||
style: { | |||
color: "#ffa1c9" | |||
} | |||
}, | |||
{ | |||
types: ["string", "attr-value", 'unit'], | |||
style: { | |||
color: "#eed371" | |||
} | |||
}, | |||
{ | |||
types: ["variable"], | |||
style: { | |||
color: "#8bc275" | |||
} | |||
}, | |||
{ | |||
types: [ | |||
"entity", | |||
"url", | |||
"control", | |||
"directive", | |||
"statement", | |||
"regex", | |||
"placeholder", | |||
], | |||
style: { | |||
color: "#ffcc99" | |||
} | |||
}, | |||
{ | |||
types: ["deleted"], | |||
style: { | |||
textDecorationLine: "line-through" | |||
} | |||
}, | |||
{ | |||
types: ["inserted"], | |||
style: { | |||
textDecorationLine: "underline" | |||
} | |||
}, | |||
{ | |||
types: ["italic"], | |||
style: { | |||
fontStyle: "italic" | |||
} | |||
}, | |||
{ | |||
types: ["important", "bold"], | |||
style: { | |||
fontWeight: "bold" | |||
} | |||
}, | |||
{ | |||
types: ["important"], | |||
style: { | |||
color: "#c4b9fe" | |||
} | |||
} | |||
] | |||
} | |||
export default theme |
@@ -2175,6 +2175,15 @@ clean-stack@^2.0.0: | |||
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" | |||
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== | |||
clipboard@^2.0.0: | |||
version "2.0.6" | |||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" | |||
integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== | |||
dependencies: | |||
good-listener "^1.2.2" | |||
select "^1.1.2" | |||
tiny-emitter "^2.0.0" | |||
code-point-at@^1.0.0: | |||
version "1.1.0" | |||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" | |||
@@ -2590,6 +2599,11 @@ define-property@^2.0.2: | |||
is-descriptor "^1.0.2" | |||
isobject "^3.0.1" | |||
delegate@^3.1.2: | |||
version "3.2.0" | |||
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" | |||
integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== | |||
delegates@^1.0.0: | |||
version "1.0.0" | |||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" | |||
@@ -3173,6 +3187,13 @@ globals@^11.1.0: | |||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" | |||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== | |||
good-listener@^1.2.2: | |||
version "1.2.2" | |||
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" | |||
integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= | |||
dependencies: | |||
delegate "^3.1.2" | |||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: | |||
version "4.2.4" | |||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" | |||
@@ -4897,11 +4918,18 @@ prebuild-install@^5.3.5: | |||
tunnel-agent "^0.6.0" | |||
which-pm-runs "^1.0.0" | |||
prism-react-renderer@^1.0.1: | |||
prism-react-renderer@^1.0.1, prism-react-renderer@^1.1.1: | |||
version "1.1.1" | |||
resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.1.1.tgz#1c1be61b1eb9446a146ca7a50b7bcf36f2a70a44" | |||
integrity sha512-MgMhSdHuHymNRqD6KM3eGS0PNqgK9q4QF5P0yoQQvpB6jNjeSAi3jcSAz0Sua/t9fa4xDOMar9HJbLa08gl9ug== | |||
prismjs@^1.22.0: | |||
version "1.22.0" | |||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.22.0.tgz#73c3400afc58a823dd7eed023f8e1ce9fd8977fa" | |||
integrity sha512-lLJ/Wt9yy0AiSYBf212kK3mM5L8ycwlyTlSxHBAneXLR0nzFMlZ5y7riFPF3E33zXOF2IH95xdY5jIyZbM9z/w== | |||
optionalDependencies: | |||
clipboard "^2.0.0" | |||
process-nextick-args@~2.0.0: | |||
version "2.0.1" | |||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" | |||
@@ -5411,6 +5439,11 @@ schema-utils@^1.0.0: | |||
ajv-errors "^1.0.0" | |||
ajv-keywords "^3.1.0" | |||
select@^1.1.2: | |||
version "1.1.2" | |||
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" | |||
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= | |||
semver@7.0.0: | |||
version "7.0.0" | |||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" | |||
@@ -5952,6 +5985,11 @@ timers-browserify@^2.0.4: | |||
dependencies: | |||
setimmediate "^1.0.4" | |||
tiny-emitter@^2.0.0: | |||
version "2.1.0" | |||
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" | |||
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== | |||
title-case@^2.1.0: | |||
version "2.1.1" | |||
resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" | |||
@@ -36,7 +36,7 @@ const propTypes = { | |||
*/ | |||
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | |||
/** | |||
* Describe of what the component represents. | |||
* Description of what the component represents. | |||
*/ | |||
label: PropTypes.string, | |||
} | |||
@@ -291,7 +291,7 @@ const Select = React.forwardRef<HTMLSelectElement, Props>( | |||
fontSize: SECONDARY_TEXT_SIZES[size!], | |||
}} | |||
> | |||
({stringify(hint)}) | |||
{stringify(hint)} | |||
</HintWrapper> | |||
)} | |||
{!multiple && ( | |||
@@ -351,7 +351,7 @@ const TextInput = React.forwardRef<HTMLInputElement | HTMLTextAreaElement, Props | |||
fontSize: SECONDARY_TEXT_SIZES[size!], | |||
}} | |||
> | |||
({stringify(hint)}) | |||
{stringify(hint)} | |||
</HintWrapper> | |||
)} | |||
{(indicator as PropTypes.ReactComponentLike) && ( | |||