Feat/improve mobile display (#843)

* Ok 1

* Finished

* Fix PR

* Fix PR

* Fix desktop

* Fix

* Fix absolute listen click outside

* console.log

* Fix according to code review

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2023-07-23 19:53:35 +02:00
committed by GitHub
parent 742791bd92
commit 21d5133564
45 changed files with 464 additions and 315 deletions

View File

@ -4,11 +4,11 @@ import NavItemsContainer from './NavItemsContainer';
import NavWorkspaceButton from './NavWorkspaceButton';
type OwnProps = {
children: JSX.Element;
children: React.ReactNode;
};
const StyledContainer = styled.div`
width: ${({ theme }) => theme.leftNavBarWidth};
width: 100%;
`;
export default function MainNavbar({ children }: OwnProps) {

View File

@ -5,8 +5,6 @@ import { useRecoilState } from 'recoil';
import { IconChevronLeft } from '@/ui/icon/index';
import { isNavbarSwitchingSizeState } from '@/ui/layout/states/isNavbarSwitchingSizeState';
import NavCollapseButton from './NavCollapseButton';
type OwnProps = {
title: string;
};
@ -50,7 +48,6 @@ export default function NavBackButton({ title }: OwnProps) {
<IconChevronLeft />
<span>{title}</span>
</IconAndButtonContainer>
<NavCollapseButton hideOnDesktop={true} />
</StyledContainer>
</>
);

View File

@ -1,14 +1,16 @@
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import {
IconLayoutSidebarLeftCollapse,
IconLayoutSidebarRightCollapse,
} from '@/ui/icon';
import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
const CollapseButton = styled.button<{ hideOnDesktop: boolean | undefined }>`
import { navbarIconSize } from '../constants';
const CollapseButton = styled.button`
align-items: center;
background: inherit;
border: 0;
@ -25,46 +27,33 @@ const CollapseButton = styled.button<{ hideOnDesktop: boolean | undefined }>`
justify-content: center;
padding: 0;
user-select: none;
width: 32px;
${(props) =>
props.hideOnDesktop &&
`@media (min-width: ${MOBILE_VIEWPORT}px) {
display:none;
}
`}
`;
type CollapseButtonProps = {
hideIfOpen?: boolean;
hideIfClosed?: boolean;
hideOnDesktop?: boolean;
direction?: 'left' | 'right';
};
export default function NavCollapseButton({
hideIfOpen,
hideOnDesktop,
direction = 'left',
}: CollapseButtonProps) {
const [isNavOpen, setIsNavOpen] = useRecoilState(isNavbarOpenedState);
const iconSize = useIsMobile()
? navbarIconSize.mobile
: navbarIconSize.desktop;
return (
<>
{isNavOpen && !hideIfOpen && (
<CollapseButton
onClick={() => setIsNavOpen(!isNavOpen)}
hideOnDesktop={hideOnDesktop}
>
<IconLayoutSidebarLeftCollapse size={16} />
{direction === 'left' ? (
<CollapseButton onClick={() => setIsNavOpen(!isNavOpen)}>
<IconLayoutSidebarLeftCollapse size={iconSize} />
</CollapseButton>
)}
{!isNavOpen && (
<CollapseButton
onClick={() => setIsNavOpen(!isNavOpen)}
hideOnDesktop={hideOnDesktop}
>
<IconLayoutSidebarRightCollapse size={16} />
) : (
<CollapseButton onClick={() => setIsNavOpen(!isNavOpen)}>
<IconLayoutSidebarRightCollapse size={iconSize} />
</CollapseButton>
)}
</>

View File

@ -1,9 +1,13 @@
import { ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
import { isNavbarOpenedState } from '../../layout/states/isNavbarOpenedState';
type OwnProps = {
label: string;
to?: string;
@ -79,21 +83,25 @@ const StyledSoonPill = styled.div`
function NavItem({ label, icon, to, onClick, active, danger, soon }: OwnProps) {
const navigate = useNavigate();
const [, setIsNavbarOpened] = useRecoilState(isNavbarOpenedState);
const isMobile = useIsMobile();
function handleItemClick() {
if (isMobile) {
setIsNavbarOpened(false);
}
const onItemClick = () => {
if (onClick) {
onClick();
return;
}
if (to) {
} else if (to) {
navigate(to);
return;
}
};
}
return (
<StyledItem
onClick={onItemClick}
onClick={handleItemClick}
active={active}
aria-selected={active}
danger={danger}

View File

@ -65,7 +65,7 @@ function NavWorkspaceButton() {
></StyledLogo>
<StyledName>{currentWorkspace?.displayName ?? 'Twenty'}</StyledName>
</LogoAndNameContainer>
<NavCollapseButton />
<NavCollapseButton direction="left" />
</StyledContainer>
);
}

View File

@ -3,10 +3,12 @@ import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useIsSubNavbarDisplayed } from '@/ui/layout/hooks/useIsSubNavbarDisplayed';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { useIsSubMenuNavbarDisplayed } from '@/ui/layout/hooks/useIsSubMenuNavbarDisplayed';
import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState';
import { isNavbarSwitchingSizeState } from '@/ui/layout/states/isNavbarSwitchingSizeState';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
import { leftNavbarWidth, leftSubMenuNavbarWidth } from '../constants';
const StyledNavbarContainer = styled(motion.div)`
align-items: end;
@ -15,35 +17,37 @@ const StyledNavbarContainer = styled(motion.div)`
flex-shrink: 0;
overflow: hidden;
padding: ${({ theme }) => theme.spacing(2)};
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: ${(props) =>
useRecoilValue(isNavbarOpenedState)
? `calc(100% - ` + props.theme.spacing(4) + `)`
: '0'};
}
`;
type NavbarProps = {
children: React.ReactNode;
layout?: string;
};
export function NavbarAnimatedContainer({ children, layout }: NavbarProps) {
export function NavbarAnimatedContainer({ children }: NavbarProps) {
const isMenuOpened = useRecoilValue(isNavbarOpenedState);
const [, setIsNavbarSwitchingSize] = useRecoilState(
isNavbarSwitchingSizeState,
);
const isSubNavbarDisplayed = useIsSubNavbarDisplayed();
const isInSubMenu = useIsSubMenuNavbarDisplayed();
const theme = useTheme();
const isMobile = useIsMobile();
const leftBarWidth = isInSubMenu
? isMobile
? leftSubMenuNavbarWidth.mobile
: leftSubMenuNavbarWidth.desktop
: isMobile
? leftNavbarWidth.mobile
: leftNavbarWidth.desktop;
return (
<StyledNavbarContainer
onAnimationComplete={() => {
setIsNavbarSwitchingSize(false);
}}
animate={{
width: isMenuOpened ? (isSubNavbarDisplayed ? '520px' : '220px') : '0',
width: isMenuOpened ? leftBarWidth : '0',
opacity: isMenuOpened ? 1 : 0,
}}
transition={{

View File

@ -1,21 +1,25 @@
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { leftNavbarWidth } from '../constants';
import NavBackButton from './NavBackButton';
import NavItemsContainer from './NavItemsContainer';
type OwnProps = {
children: JSX.Element;
children: React.ReactNode;
backButtonTitle: string;
};
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
padding-top: ${({ theme }) => theme.spacing(6)};
width: 220px;
padding-top: ${({ theme }) => theme.spacing(2)};
width: ${({ theme }) => (useIsMobile() ? '100%' : leftNavbarWidth.desktop)};
`;
export default function SubNavbar({ children, backButtonTitle }: OwnProps) {
export default function SubMenuNavbar({ children, backButtonTitle }: OwnProps) {
return (
<StyledContainer>
<NavBackButton title={backButtonTitle} />

View File

@ -0,0 +1,14 @@
export const leftNavbarWidth = {
mobile: 'calc(100% - 16px)',
desktop: '220px',
};
export const leftSubMenuNavbarWidth = {
mobile: 'calc(100% - 16px)',
desktop: '520px',
};
export const navbarIconSize = {
mobile: 18,
desktop: 16,
};