@@ -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 * as React from 'react' | ||||
import Link from 'next/link' | import Link from 'next/link' | ||||
import styled from 'styled-components' | import styled from 'styled-components' | ||||
import NavLink from './NavLink/NavLink' | |||||
const StyledLink = styled('a')({ | const StyledLink = styled('a')({ | ||||
display: 'block', | display: 'block', | ||||
@@ -11,8 +12,9 @@ const Container = styled('span')({ | |||||
display: 'flex', | display: 'flex', | ||||
alignItems: 'center', | alignItems: 'center', | ||||
padding: '0 1rem', | padding: '0 1rem', | ||||
height: '2rem', | |||||
height: '100%', | |||||
margin: '0 0 0 auto', | margin: '0 0 0 auto', | ||||
boxSizing: 'border-box', | |||||
'@media (min-width: 720px)': { | '@media (min-width: 720px)': { | ||||
maxWidth: 360, | maxWidth: 360, | ||||
}, | }, | ||||
@@ -66,11 +68,10 @@ const Nav = ({ | |||||
href={value} | href={value} | ||||
passHref | passHref | ||||
> | > | ||||
<StyledLink> | |||||
<Container> | |||||
{key} | |||||
</Container> | |||||
</StyledLink> | |||||
<NavLink | |||||
title={key} | |||||
enclosingComponent={Container} | |||||
/> | |||||
</Link> | </Link> | ||||
) | ) | ||||
} | } | ||||
@@ -79,11 +80,10 @@ const Nav = ({ | |||||
href={data} | href={data} | ||||
passHref | passHref | ||||
> | > | ||||
<StyledLink> | |||||
<Container> | |||||
{data} | |||||
</Container> | |||||
</StyledLink> | |||||
<NavLink | |||||
title={data} | |||||
enclosingComponent={Container} | |||||
/> | |||||
</Link> | </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, | LiveEditor, | ||||
LivePreview | LivePreview | ||||
} from 'react-live' | } from 'react-live' | ||||
import pascalCase from 'pascal-case' | |||||
import styled from 'styled-components' | import styled from 'styled-components' | ||||
const Figure = styled('figure')({ | const Figure = styled('figure')({ | ||||
@@ -23,24 +24,24 @@ const Playground = ({ | |||||
.join('\n') | .join('\n') | ||||
return ( | 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 Nav from './Nav' | ||||
import Link from 'next/link' | import Link from 'next/link' | ||||
import * as React from 'react' | import * as React from 'react' | ||||
import { Checkbox } from '@tesseract-design/react-common' | |||||
const StyledLink = styled('a')({ | const StyledLink = styled('a')({ | ||||
display: 'block', | display: 'block', | ||||
@@ -16,6 +15,7 @@ const Container = styled('span')({ | |||||
padding: '0 1rem', | padding: '0 1rem', | ||||
height: '2rem', | height: '2rem', | ||||
margin: '0 0 0 auto', | margin: '0 0 0 auto', | ||||
boxSizing: 'border-box', | |||||
'@media (min-width: 720px)': { | '@media (min-width: 720px)': { | ||||
maxWidth: 360, | maxWidth: 360, | ||||
}, | }, | ||||
@@ -41,13 +41,17 @@ const Base = styled('aside')({ | |||||
}, | }, | ||||
}) | }) | ||||
const NavWrapper = styled('nav')({ | |||||
'--size-link': '3rem', | |||||
}) | |||||
const Sidebar = ({ | const Sidebar = ({ | ||||
data, | data, | ||||
brand: Brand, | brand: Brand, | ||||
}) => { | }) => { | ||||
const [dark, setDark] = React.useState(false) | const [dark, setDark] = React.useState(false) | ||||
const toggleDarkMode = b => () => { | |||||
const toggleDarkMode = (b: boolean) => () => { | |||||
setDark(b) | setDark(b) | ||||
} | } | ||||
@@ -74,7 +78,7 @@ const Sidebar = ({ | |||||
onChange={toggleDarkMode(!dark)} | onChange={toggleDarkMode(!dark)} | ||||
/> | /> | ||||
</Container> | </Container> | ||||
<nav> | |||||
<NavWrapper> | |||||
<Link | <Link | ||||
href="/" | href="/" | ||||
passHref | passHref | ||||
@@ -88,7 +92,7 @@ const Sidebar = ({ | |||||
<Nav | <Nav | ||||
data={data.nav} | data={data.nav} | ||||
/> | /> | ||||
</nav> | |||||
</NavWrapper> | |||||
</Base> | </Base> | ||||
) | ) | ||||
} | } | ||||
@@ -34,7 +34,7 @@ act as guide to the user on how long the expected input values are. | |||||
components={{ TextInput }} | components={{ TextInput }} | ||||
code={` | code={` | ||||
<form> | <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> | </form> | ||||
`} | `} | ||||
/> | /> | ||||
@@ -4,6 +4,8 @@ import styled from 'styled-components' | |||||
import stringify from '../../services/stringify' | import stringify from '../../services/stringify' | ||||
import { Size, SizeMap } from '../../services/utilities' | import { Size, SizeMap } from '../../services/utilities' | ||||
// TODO implement web-client text inputs! | |||||
const MIN_HEIGHTS: SizeMap<string | number> = { | const MIN_HEIGHTS: SizeMap<string | number> = { | ||||
small: '2.5rem', | small: '2.5rem', | ||||
medium: '3rem', | medium: '3rem', | ||||