@@ -0,0 +1,50 @@ | |||
import * as React from 'react' | |||
import * as PropTypes from 'prop-types' | |||
import styled from 'styled-components' | |||
import { Icon } from '@tesseract-design/react-common' | |||
const Image = styled('img')({ | |||
display: 'block', | |||
maxWidth: '100%', | |||
maxHeight: '100%', | |||
}) | |||
export type MenuGraphicsKind = 'icon' | 'image' | |||
export const propTypes = { | |||
kind: PropTypes.oneOf<MenuGraphicsKind>(['icon', 'image']).isRequired, | |||
alt: PropTypes.string, | |||
id: PropTypes.string.isRequired, | |||
} | |||
type Props = PropTypes.InferProps<typeof propTypes> | |||
const MenuGraphics: React.FC<Props> = ({ | |||
kind, | |||
alt, | |||
id, | |||
}) => { | |||
switch (kind) { | |||
case 'icon': | |||
return ( | |||
<Icon | |||
name={id} | |||
/> | |||
) | |||
case 'image': | |||
return ( | |||
<Image | |||
src={id} | |||
alt={alt} | |||
/> | |||
) | |||
default: | |||
break | |||
} | |||
return null | |||
} | |||
MenuGraphics.propTypes = propTypes | |||
export default MenuGraphics |
@@ -1,6 +1,7 @@ | |||
import * as React from 'react' | |||
import Link from 'next/link' | |||
import styled from 'styled-components' | |||
import NavLink from './NavLink/NavLink' | |||
const StyledLink = styled('a')({ | |||
display: 'block', | |||
@@ -11,8 +12,9 @@ const Container = styled('span')({ | |||
display: 'flex', | |||
alignItems: 'center', | |||
padding: '0 1rem', | |||
height: '2rem', | |||
height: '100%', | |||
margin: '0 0 0 auto', | |||
boxSizing: 'border-box', | |||
'@media (min-width: 720px)': { | |||
maxWidth: 360, | |||
}, | |||
@@ -66,11 +68,10 @@ const Nav = ({ | |||
href={value} | |||
passHref | |||
> | |||
<StyledLink> | |||
<Container> | |||
{key} | |||
</Container> | |||
</StyledLink> | |||
<NavLink | |||
title={key} | |||
enclosingComponent={Container} | |||
/> | |||
</Link> | |||
) | |||
} | |||
@@ -79,11 +80,10 @@ const Nav = ({ | |||
href={data} | |||
passHref | |||
> | |||
<StyledLink> | |||
<Container> | |||
{data} | |||
</Container> | |||
</StyledLink> | |||
<NavLink | |||
title={data} | |||
enclosingComponent={Container} | |||
/> | |||
</Link> | |||
) | |||
} | |||
@@ -0,0 +1,150 @@ | |||
import * as React from 'react' | |||
import * as PropTypes from 'prop-types' | |||
import styled from 'styled-components' | |||
import { Icon } from '@tesseract-design/react-common' | |||
import MenuGraphics, { propTypes as menuGraphicsPropTypes } from '../MenuGraphics/MenuGraphics' | |||
const Link = styled('a')({ | |||
display: 'block', | |||
height: 'var(--size-link, 4rem)', | |||
textDecoration: 'none', | |||
position: 'relative', | |||
'::before': { | |||
display: 'block', | |||
content: "''", | |||
position: 'absolute', | |||
top: 0, | |||
left: 0, | |||
width: '100%', | |||
height: '100%', | |||
backgroundColor: 'currentColor', | |||
opacity: 0, | |||
zIndex: -1, | |||
}, | |||
'::after': { | |||
display: 'block', | |||
content: "''", | |||
position: 'absolute', | |||
top: 0, | |||
left: 0, | |||
width: '0.25rem', | |||
height: '100%', | |||
backgroundColor: 'currentColor', | |||
opacity: 0, | |||
zIndex: -1, | |||
}, | |||
':hover::before': { | |||
opacity: 0.25, | |||
}, | |||
':hover::after': { | |||
opacity: 0.5, | |||
}, | |||
}) | |||
const LinkText = styled('span')({ | |||
alignSelf: 'center', | |||
display: 'block', | |||
lineHeight: 1.25, | |||
gridColumnStart: 2, | |||
':first-child': { | |||
gridColumnStart: 1, | |||
}, | |||
':last-child': { | |||
gridColumnEnd: 4, | |||
}, | |||
}) | |||
const LinkTitle = styled('strong')({ | |||
display: 'block', | |||
}) | |||
const LinkSubtitle = styled('span')({ | |||
display: 'block', | |||
}) | |||
const IndicatorContainer = styled('span')({ | |||
alignSelf: 'center', | |||
lineHeight: 0, | |||
}) | |||
const MenuGraphicsContainer = styled('span')({ | |||
display: 'block', | |||
padding: '0.5rem', | |||
lineHeight: 0, | |||
}) | |||
export const basePropTypes = { | |||
as: PropTypes.string, | |||
href: PropTypes.string.isRequired, | |||
title: PropTypes.string.isRequired, | |||
subtitle: PropTypes.string, | |||
graphics: PropTypes.shape(menuGraphicsPropTypes), | |||
indicator: PropTypes.string, | |||
} | |||
const propTypes = { | |||
...basePropTypes, | |||
enclosingComponent: PropTypes.elementType, | |||
as: PropTypes.elementType, | |||
} | |||
type Props = PropTypes.InferProps<typeof propTypes> | |||
const NavLink = React.forwardRef<HTMLAnchorElement, Props>(( | |||
{ | |||
enclosingComponent: EnclosingComponent = React.Fragment, | |||
href, | |||
title, | |||
graphics, | |||
subtitle, | |||
indicator, | |||
onClick, | |||
}, | |||
ref | |||
) => ( | |||
<Link | |||
ref={ref} | |||
href={href} | |||
onClick={onClick} | |||
> | |||
<EnclosingComponent> | |||
{ | |||
graphics as object | |||
&& ( | |||
<MenuGraphicsContainer> | |||
<MenuGraphics | |||
{...graphics} | |||
/> | |||
</MenuGraphicsContainer> | |||
) | |||
} | |||
<LinkText> | |||
<LinkTitle> | |||
{title} | |||
</LinkTitle> | |||
{ | |||
subtitle as string | |||
&& ( | |||
<LinkSubtitle> | |||
{subtitle} | |||
</LinkSubtitle> | |||
) | |||
} | |||
</LinkText> | |||
{ | |||
indicator as string | |||
&& ( | |||
<IndicatorContainer> | |||
<Icon | |||
name={indicator} | |||
/> | |||
</IndicatorContainer> | |||
) | |||
} | |||
</EnclosingComponent> | |||
</Link> | |||
)) | |||
NavLink.propTypes = propTypes | |||
export default NavLink |
@@ -4,6 +4,7 @@ import { | |||
LiveEditor, | |||
LivePreview | |||
} from 'react-live' | |||
import pascalCase from 'pascal-case' | |||
import styled from 'styled-components' | |||
const Figure = styled('figure')({ | |||
@@ -23,24 +24,24 @@ const Playground = ({ | |||
.join('\n') | |||
return ( | |||
<div> | |||
<Figure> | |||
{ | |||
label | |||
&& ( | |||
<figcaption> | |||
{label} | |||
</figcaption> | |||
) | |||
} | |||
<div> | |||
<LiveProvider code={normalizedCode} scope={components}> | |||
<LivePreview /> | |||
<LiveEditor /> | |||
</LiveProvider> | |||
</div> | |||
</Figure> | |||
</div> | |||
<Figure> | |||
{ | |||
label | |||
&& ( | |||
<figcaption> | |||
{label} | |||
</figcaption> | |||
) | |||
} | |||
<div> | |||
<LiveProvider code={normalizedCode} scope={{ | |||
...components, | |||
}}> | |||
<LivePreview /> | |||
<LiveEditor /> | |||
</LiveProvider> | |||
</div> | |||
</Figure> | |||
) | |||
} | |||
@@ -2,7 +2,6 @@ import styled from 'styled-components' | |||
import Nav from './Nav' | |||
import Link from 'next/link' | |||
import * as React from 'react' | |||
import { Checkbox } from '@tesseract-design/react-common' | |||
const StyledLink = styled('a')({ | |||
display: 'block', | |||
@@ -16,6 +15,7 @@ const Container = styled('span')({ | |||
padding: '0 1rem', | |||
height: '2rem', | |||
margin: '0 0 0 auto', | |||
boxSizing: 'border-box', | |||
'@media (min-width: 720px)': { | |||
maxWidth: 360, | |||
}, | |||
@@ -41,13 +41,17 @@ const Base = styled('aside')({ | |||
}, | |||
}) | |||
const NavWrapper = styled('nav')({ | |||
'--size-link': '3rem', | |||
}) | |||
const Sidebar = ({ | |||
data, | |||
brand: Brand, | |||
}) => { | |||
const [dark, setDark] = React.useState(false) | |||
const toggleDarkMode = b => () => { | |||
const toggleDarkMode = (b: boolean) => () => { | |||
setDark(b) | |||
} | |||
@@ -74,7 +78,7 @@ const Sidebar = ({ | |||
onChange={toggleDarkMode(!dark)} | |||
/> | |||
</Container> | |||
<nav> | |||
<NavWrapper> | |||
<Link | |||
href="/" | |||
passHref | |||
@@ -88,7 +92,7 @@ const Sidebar = ({ | |||
<Nav | |||
data={data.nav} | |||
/> | |||
</nav> | |||
</NavWrapper> | |||
</Base> | |||
) | |||
} | |||
@@ -34,7 +34,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', }}><TextInput label="Full name" hint="given and family name" /></span> and I live in <span style={{ display: 'inline-block', width: '24rem', }}><TextInput label="Address" hint="city, state and country" /></span>. | |||
I am <span style={{ display: 'inline-block', width: '16rem', verticalAlign: 'middle', }}><TextInput label="Full name" border hint="given and family name" /></span> and I live in <span style={{ display: 'inline-block', width: '24rem', verticalAlign: 'middle', }}><TextInput border label="Address" hint="city, state and country" /></span>. | |||
</form> | |||
`} | |||
/> | |||
@@ -4,6 +4,8 @@ import styled from 'styled-components' | |||
import stringify from '../../services/stringify' | |||
import { Size, SizeMap } from '../../services/utilities' | |||
// TODO implement web-client text inputs! | |||
const MIN_HEIGHTS: SizeMap<string | number> = { | |||
small: '2.5rem', | |||
medium: '3rem', | |||