Skip to content

Commit

Permalink
feat(sidebar): render as button menu for < sm viewports (#213)
Browse files Browse the repository at this point in the history
Preserve as much screen real estate as possible by hiding the sidebar
behind a 25% transparent menu button for viewports smaller than `sm`

- For < sm viewports,
  - ensure page layouts are at absolute top left
  - move AppNavbar title to centre

- Use breakpoint responsive values to render either a sidebar,
  or a hidden sidebar revealed by an icon button
  • Loading branch information
LoneRifle authored Nov 7, 2023
1 parent 6834aaf commit 953fe0f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 13 deletions.
9 changes: 8 additions & 1 deletion src/components/AppNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,19 @@ export const AppNavbar = (): JSX.Element => {
justify="space-between"
align="center"
px={{ base: '1.5rem', md: '1.8rem', xl: '2rem' }}
pl={{ base: `calc(1rem + ${ADMIN_NAVBAR_HEIGHT})`, sm: '1.5rem' }}
py="0.375rem"
bg="white"
borderBottomWidth="1px"
borderColor="base.divider.medium"
transition="padding 0.1s"
>
<Link as={NextLink} href="/home">
<Link
as={NextLink}
href="/home"
mx={{ base: 'auto', sm: 0 }}
transition="margin 0.1s"
>
<Image
// This component can only be used if this is an application created by OGP.
src="/assets/restricted-ogp-logo-full.svg"
Expand Down
71 changes: 63 additions & 8 deletions src/components/DashSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
import { Box, Flex } from '@chakra-ui/react'
import {
Box,
CloseButton,
IconButton,
Flex,
useBreakpoint,
} from '@chakra-ui/react'
import {
SidebarContainer,
SidebarItem,
useIsMobile,
} from '@opengovsg/design-system-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useMemo } from 'react'
import { BiFace, BiHomeSmile } from 'react-icons/bi'
import { useMemo, useState } from 'react'
import { BiFace, BiHomeSmile, BiMenu } from 'react-icons/bi'
import { ADMIN_DASHBAR_WIDTHS, ADMIN_NAVBAR_HEIGHT } from '~/constants/layouts'
import { useMe } from '~/features/me/api'
import { HOME, PROFILE, SETTINGS_PROFILE } from '~/lib/routes'

export const DashSidebar = () => {
const [showWhenSmallMobile, setShowWhenSmallMobile] = useState(false)
const isMobile = useIsMobile()
const { me } = useMe()
const breakpointValue = useBreakpoint()
const { pathname, query } = useRouter()

const mobileButtonProps = {
zIndex: 'overlay',
colorScheme: 'neutral',
border: '0px',
borderRight: '1px',
boxSize: ADMIN_NAVBAR_HEIGHT,
borderRadius: '0px',
borderColor: 'base.divider.medium',
display: { base: 'inline-flex', sm: 'none' },
}

const isProfileActive = useMemo(() => {
if (pathname === SETTINGS_PROFILE) return true
if (
Expand All @@ -26,19 +45,53 @@ export const DashSidebar = () => {
return true
}, [pathname, query.username, me?.username])

const showText = isMobile && breakpointValue === 'sm'

return (
<Box position="relative">
<Box
position={{ base: 'fixed', sm: 'relative' }}
zIndex={{ base: '20', sm: '1' }}
>
{showWhenSmallMobile ? (
<CloseButton
{...mobileButtonProps}
aria-label="Close sidebar"
onClick={() => setShowWhenSmallMobile(false)}
bg="slate.100"
/>
) : (
<IconButton
{...mobileButtonProps}
aria-label="Open sidebar"
onClick={() => setShowWhenSmallMobile(true)}
bg="white"
variant="clear"
icon={<BiMenu size="1.25rem" />}
/>
)}
<Flex
bg="white"
borderTop={showWhenSmallMobile ? '1px solid' : undefined}
borderRight="1px solid"
borderColor="base.divider.medium"
pos="fixed"
width={ADMIN_DASHBAR_WIDTHS}
h={`calc(var(--chakra-vh) - ${ADMIN_NAVBAR_HEIGHT})`}
opacity={{
base: showWhenSmallMobile ? 1 : 0,
sm: 1,
}}
flexDir="column"
justify="space-between"
zIndex="2"
top={ADMIN_NAVBAR_HEIGHT}
pt={0}
pb="0.5rem"
w={{ base: showWhenSmallMobile ? '100%' : 0, sm: 'fit-content' }}
transition={{
base: 'opacity 0.2s, width 0.2s',
sm: undefined,
}}
>
<SidebarContainer size="sm">
<SidebarItem
Expand All @@ -47,21 +100,23 @@ export const DashSidebar = () => {
href={HOME}
isActive={pathname === HOME}
title="Home"
px={{ base: '0.75rem', md: '1rem' }}
px={{ base: '1.125rem', sm: '0.75rem', md: '1rem' }}
borderRadius={{ base: 0, md: 'md' }}
onClick={() => setShowWhenSmallMobile(false)}
>
{isMobile ? '' : 'Home'}
{showText ? '' : 'Home'}
</SidebarItem>
<SidebarItem
icon={BiFace}
as={Link}
href={`${PROFILE}/${me?.username}`}
isActive={isProfileActive}
title="Profile"
px={{ base: '0.75rem', md: '1rem' }}
px={{ base: '1.125rem', sm: '0.75rem', md: '1rem' }}
borderRadius={{ base: 0, md: 'md' }}
onClick={() => setShowWhenSmallMobile(false)}
>
{isMobile ? '' : 'Profile'}
{showText ? '' : 'Profile'}
</SidebarItem>
</SidebarContainer>
</Flex>
Expand Down
14 changes: 12 additions & 2 deletions src/pages/home.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { Box, Flex } from '@chakra-ui/react'
import { SkeletonPostList } from '~/components/SkeletonPostList'
import Suspense from '~/components/Suspense'
import { APP_GRID_COLUMN, APP_GRID_TEMPLATE_COLUMN } from '~/constants/layouts'
import {
ADMIN_NAVBAR_HEIGHT,
APP_GRID_COLUMN,
APP_GRID_TEMPLATE_COLUMN,
} from '~/constants/layouts'
import { NewPostBanner, PostList } from '~/features/home/components'
import { type NextPageWithLayout } from '~/lib/types'
import { AppGrid } from '~/templates/AppGrid'
import { AdminLayout } from '~/templates/layouts/AdminLayout'

const Home: NextPageWithLayout = () => {
return (
<Flex w="100%" flexDir="column">
<Flex
w="100%"
flexDir="column"
position={{ base: 'absolute', sm: 'inherit' }}
left={{ base: 0, sm: undefined }}
minH={`calc(100% - ${ADMIN_NAVBAR_HEIGHT})`}
>
<AppGrid
templateColumns={APP_GRID_TEMPLATE_COLUMN}
px={{ base: '1rem', lg: 0 }}
Expand Down
11 changes: 9 additions & 2 deletions src/templates/layouts/ProfileLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ const _ProfileLayout: NextPageWithLayout['getLayout'] = (page) => {
const username = String(query.username)

return (
<Flex w="100%" flexDir="column">
<Flex
w="100%"
flexDir="column"
position={{ base: 'absolute', sm: 'inherit' }}
left={{ base: 0, sm: undefined }}
>
<AppGrid
templateColumns={APP_GRID_TEMPLATE_COLUMN}
bg="base.canvas.brand-subtle"
Expand All @@ -33,7 +38,9 @@ const _ProfileLayout: NextPageWithLayout['getLayout'] = (page) => {
<ProfileTabs username={username} />
</Box>
<Divider gridColumn={{ base: '1/5', md: '1/12' }} h="1px" />
<Box gridColumn={APP_GRID_COLUMN}>{page}</Box>
<Box gridColumn={APP_GRID_COLUMN} minH="100%">
{page}
</Box>
</AppGrid>
</Flex>
)
Expand Down

1 comment on commit 953fe0f

@vercel
Copy link

@vercel vercel bot commented on 953fe0f Nov 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.