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:
@ -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) {
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -65,7 +65,7 @@ function NavWorkspaceButton() {
|
||||
></StyledLogo>
|
||||
<StyledName>{currentWorkspace?.displayName ?? 'Twenty'}</StyledName>
|
||||
</LogoAndNameContainer>
|
||||
<NavCollapseButton />
|
||||
<NavCollapseButton direction="left" />
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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={{
|
||||
|
||||
@ -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} />
|
||||
14
front/src/modules/ui/navbar/constants/index.ts
Normal file
14
front/src/modules/ui/navbar/constants/index.ts
Normal 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,
|
||||
};
|
||||
Reference in New Issue
Block a user