Browse Source

Improve tablet layout, add sidebar animation

Make the sidebar not occupy the whole screen width. Also add transitions for sidebar opening/closing.
feature/transactions
TheoryOfNekomata 4 years ago
parent
commit
47bfd74297
4 changed files with 195 additions and 88 deletions
  1. +127
    -51
      src/components/Navbar/Navbar.tsx
  2. +1
    -0
      src/components/PrimaryNavItem/PrimaryNavItem.tsx
  3. +49
    -29
      src/components/SecondaryNavItem/SecondaryNavItem.tsx
  4. +18
    -8
      src/pages/notes.tsx

+ 127
- 51
src/components/Navbar/Navbar.tsx View File

@@ -1,7 +1,8 @@
import * as React from 'react' import * as React from 'react'
import styled from 'styled-components'
import styled, { createGlobalStyle } from 'styled-components'
import SecondaryNavItem from '../SecondaryNavItem/SecondaryNavItem' import SecondaryNavItem from '../SecondaryNavItem/SecondaryNavItem'
import PrimaryNavItem from '../PrimaryNavItem/PrimaryNavItem' import PrimaryNavItem from '../PrimaryNavItem/PrimaryNavItem'
import Link from 'next/link'


const Base = styled('aside')({ const Base = styled('aside')({
width: 360, width: 360,
@@ -59,9 +60,37 @@ const PrimaryNavItemGroup = styled('div')({
}) })


const SecondaryNavItems = styled('nav')({ const SecondaryNavItems = styled('nav')({
width: '100%',
height: '100%',
position: 'fixed',
top: 0,
left: 0,
paddingBottom: '4rem',
boxSizing: 'border-box',
pointerEvents: 'none',
'@media (min-width: 1080px)': {
position: 'relative',
top: 'auto',
left: 'auto',
flex: 'auto',
width: 'auto',
paddingBottom: 0,
pointerEvents: 'initial',
},
})

const SecondaryNavItemsFg = styled('div')({
height: '100%', height: '100%',
position: 'relative',
backgroundColor: 'var(--color-bg)', backgroundColor: 'var(--color-bg)',
width: 360,
position: 'absolute',
top: 0,
left: -360,
paddingBottom: '4rem',
boxSizing: 'border-box',
transitionProperty: 'left',
transitionDuration: '350ms',
transitionTimingFunction: 'ease-out',
'::before': { '::before': {
content: "''", content: "''",
display: 'block', display: 'block',
@@ -73,27 +102,34 @@ const SecondaryNavItems = styled('nav')({
backgroundColor: 'black', backgroundColor: 'black',
opacity: 0.03125, opacity: 0.03125,
}, },
'@media (min-width: 1080px)': {
flex: 'auto',
},
})

const VisibleSecondaryNavItems = styled(SecondaryNavItems)({
position: 'fixed',
top: 0,
left: 0,
width: '100%',
paddingBottom: '4rem',
boxSizing: 'border-box',
'@media (min-width: 1080px)': { '@media (min-width: 1080px)': {
position: 'relative', position: 'relative',
top: 'auto', top: 'auto',
left: 'auto', left: 'auto',
flex: 'auto',
width: 'auto', width: 'auto',
paddingBottom: 0, paddingBottom: 0,
}, },
}) })


const SecondaryNavItemsBg = styled('a')({
opacity: 0,
transitionProperty: 'opacity',
transitionDuration: '350ms',
transitionTimingFunction: 'ease-out',
'::before': {
content: "''",
display: 'block',
top: 0,
left: 0,
width: '100%',
height: '100%',
position: 'absolute',
backgroundColor: 'black',
opacity: 0.75,
},
})

const SecondaryNavItemsOverflow = styled('div')({ const SecondaryNavItemsOverflow = styled('div')({
overflow: 'auto', overflow: 'auto',
width: '100%', width: '100%',
@@ -116,15 +152,40 @@ const NavbarContainer = styled('div')({
maxWidth: 360, maxWidth: 360,
}) })


const SecondaryVisibleDummy = styled('div')({
display: 'none',
})

const Visible = createGlobalStyle({
[`div + ${SecondaryNavItems}`]: {
pointerEvents: 'initial',
},
[`div + ${SecondaryNavItems} ${SecondaryNavItemsFg}`]: {
left: 0,
},
[`div + ${SecondaryNavItems} ${SecondaryNavItemsBg}`]: {
opacity: 1,
},
'@media (min-width: 1080px)': {
[`div + ${SecondaryNavItems} ${SecondaryNavItemsFg}`]: {
left: 'auto',
},
[`div + ${SecondaryNavItems} ${SecondaryNavItemsBg}`]: {
opacity: 0,
},
}
})

const Navbar = ({ const Navbar = ({
closeHref,
secondaryVisible, secondaryVisible,
primaryItemsStart = [], primaryItemsStart = [],
primaryItemsEnd = [], primaryItemsEnd = [],
secondaryItemsHeader = [], secondaryItemsHeader = [],
secondaryItems = [], secondaryItems = [],
}) => {
const SecondaryNavItemsComponent = secondaryVisible ? VisibleSecondaryNavItems : SecondaryNavItems
return (
}) => (
<React.Fragment>
<Visible />
<Base> <Base>
<NavbarContainer> <NavbarContainer>
<NavbarItems> <NavbarItems>
@@ -165,42 +226,57 @@ const Navbar = ({
} }
</PrimaryNavItemsContainer> </PrimaryNavItemsContainer>
</PrimaryNavItems> </PrimaryNavItems>
<SecondaryNavItemsComponent>
<SecondaryNavItemsOverflow>
{
secondaryItemsHeader.map(i => (
<SecondaryNavItem
key={i.id}
active={i.active}
href={i.href}
replace={i.replace}
iconName={i.iconName}
title={i.title}
subtitle={i.subtitle}
actions={i.actions}
/>
))
}
{
secondaryItems.map(i => (
<SecondaryNavItem
key={i.id}
active={i.active}
href={i.href}
replace={i.replace}
iconName={i.iconName}
title={i.title}
subtitle={i.subtitle}
actions={i.actions}
/>
))
}
</SecondaryNavItemsOverflow>
</SecondaryNavItemsComponent>
{
secondaryVisible
&& (
<SecondaryVisibleDummy />
)
}
<SecondaryNavItems>
<Link
href={closeHref}
passHref
shallow
>
<SecondaryNavItemsBg />
</Link>
<SecondaryNavItemsFg>
<SecondaryNavItemsOverflow>
{
secondaryItemsHeader.map(i => (
<SecondaryNavItem
key={i.id}
active={i.active}
href={i.href}
replace={i.replace}
iconName={i.iconName}
title={i.title}
subtitle={i.subtitle}
actions={i.actions}
/>
))
}
{
secondaryItems.map(i => (
<SecondaryNavItem
key={i.id}
active={i.active}
href={i.href}
replace={i.replace}
iconName={i.iconName}
title={i.title}
subtitle={i.subtitle}
actions={i.actions}
/>
))
}
</SecondaryNavItemsOverflow>
</SecondaryNavItemsFg>
</SecondaryNavItems>
</NavbarItems> </NavbarItems>
</NavbarContainer> </NavbarContainer>
</Base> </Base>
)
}
</React.Fragment>
)


export default Navbar export default Navbar

+ 1
- 0
src/components/PrimaryNavItem/PrimaryNavItem.tsx View File

@@ -89,6 +89,7 @@ const PrimaryNavItem: React.FC<Props> = ({
<Link <Link
href={href} href={href}
passHref passHref
shallow
> >
<ClickArea <ClickArea
title={title} title={title}


+ 49
- 29
src/components/SecondaryNavItem/SecondaryNavItem.tsx View File

@@ -8,31 +8,54 @@ const NoteLink = styled('a')({
display: 'flex', display: 'flex',
textDecoration: 'none', textDecoration: 'none',
color: 'inherit', color: 'inherit',
height: '4rem',
alignItems: 'center', alignItems: 'center',
position: 'relative', position: 'relative',
flex: 'auto',
padding: '0 1rem',
boxSizing: 'border-box',
}) })


const NoteLinkPrimary = styled('div')({
const NoteLinkPrimary = styled('span')({
display: 'block', display: 'block',
flex: 'auto',
}) })


const NoteLinkTitle = styled('strong')({ const NoteLinkTitle = styled('strong')({
verticalAlign: 'middle',
display: 'block',
height: '1.25em',
position: 'relative',
})

const NoteLinkTitleOverflow = styled('span')({
whiteSpace: 'nowrap',
overflow: 'hidden',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
textOverflow: 'ellipsis',
}) })


const LinkContainer = styled('div')({ const LinkContainer = styled('div')({
position: 'relative', position: 'relative',
color: 'var(--color-primary, blue)', color: 'var(--color-primary, blue)',
display: 'flex',
alignItems: 'stretch',
height: '4rem',
}) })


const NoteActions = styled('div')({ const NoteActions = styled('div')({
display: 'flex', display: 'flex',
position: 'absolute',
alignItems: 'stretch', alignItems: 'stretch',
top: 0,
right: 0,
height: '100%', height: '100%',
position: 'relative',
'@media (min-width: 1080px)': {
opacity: 0,
[`${LinkContainer}:hover &`]: {
opacity: 1,
},
},
}) })


const NoteAction = styled('button')({ const NoteAction = styled('button')({
@@ -43,6 +66,7 @@ const NoteAction = styled('button')({
color: 'inherit', color: 'inherit',
cursor: 'pointer', cursor: 'pointer',
outline: 0, outline: 0,
padding: 0,
}) })


const NoteLinkBackground = styled('span')({ const NoteLinkBackground = styled('span')({
@@ -74,11 +98,6 @@ const IconContainer = styled('span')({
marginRight: '0.5rem', marginRight: '0.5rem',
}) })


const NavbarItemContent = styled('span')({
padding: '0 1rem',
boxSizing: 'border-box',
})

const PostMeta = styled('small')({ const PostMeta = styled('small')({
opacity: 0.5, opacity: 0.5,
height: '1.25rem', height: '1.25rem',
@@ -122,26 +141,27 @@ const SecondaryNavItem: React.FC<Props> = ({
href={href} href={href}
replace={replace} replace={replace}
passHref passHref
shallow
> >
<NoteLink> <NoteLink>
<NavbarItemContent>
<NoteLinkPrimary>
<IconContainer>
{
iconName
&& (
<Icon
name={iconName}
/>
)
}
</IconContainer>
<NoteLinkTitle
style={{ opacity: title.length > 0 ? 1 : 0.5, }}
>
<IconContainer>
{
iconName
&& (
<Icon
name={iconName}
/>
)
}
</IconContainer>
<NoteLinkPrimary>
<NoteLinkTitle
style={{ opacity: title.length > 0 ? 1 : 0.5, }}
>
<NoteLinkTitleOverflow>
{title.length > 0 ? title : '(untitled)'} {title.length > 0 ? title : '(untitled)'}
</NoteLinkTitle>
</NoteLinkPrimary>
</NoteLinkTitleOverflow>
</NoteLinkTitle>
{ {
subtitle subtitle
&& ( && (
@@ -153,7 +173,7 @@ const SecondaryNavItem: React.FC<Props> = ({
</React.Fragment> </React.Fragment>
) )
} }
</NavbarItemContent>
</NoteLinkPrimary>
</NoteLink> </NoteLink>
</Link> </Link>
{ {


+ 18
- 8
src/pages/notes.tsx View File

@@ -91,6 +91,19 @@ const Notes = ({ id: idProp }) => {
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
<Navbar <Navbar
closeHref={{
pathname: router.pathname,
query: Object
.entries(router.query)
.filter(([key]) => key !== 'navbar')
.reduce(
(theQuery, [key, value]) => ({
...theQuery,
[key]: value,
}),
{}
),
}}
secondaryVisible={Boolean(router.query.navbar)} secondaryVisible={Boolean(router.query.navbar)}
primaryItemsStart={[ primaryItemsStart={[
{ {
@@ -213,14 +226,11 @@ const Notes = ({ id: idProp }) => {
replace: true, replace: true,
title: n.title.trim(), title: n.title.trim(),
subtitle: ( subtitle: (
<React.Fragment>
{'Last updated '}
<time
dateTime={new Date(n.updatedAt).toISOString()}
>
{formatDate(new Date(n.updatedAt))}
</time>
</React.Fragment>
<time
dateTime={new Date(n.updatedAt).toISOString()}
>
{formatDate(new Date(n.updatedAt))}
</time>
), ),
actions: [ actions: [
{ {


Loading…
Cancel
Save