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

@ -1,5 +1,3 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from '@emotion/styled';
import { AnimatePresence, LayoutGroup } from 'framer-motion';
import { useRecoilValue } from 'recoil';
@ -11,10 +9,9 @@ import { CommandMenu } from '@/command-menu/components/CommandMenu';
import { NavbarAnimatedContainer } from '@/ui/navbar/components/NavbarAnimatedContainer';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
import { AppNavbar } from '~/AppNavbar';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { CompaniesMockMode } from '~/pages/companies/CompaniesMockMode';
import { AppPath } from '../../../types/AppPath';
import { useAutoNavigateOnboarding } from '../hooks/useAutoNavigateOnboarding';
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
const StyledLayout = styled.div`
@ -46,72 +43,30 @@ type OwnProps = {
};
export function DefaultLayout({ children }: OwnProps) {
const navigate = useNavigate();
const isMatchingLocation = useIsMatchingLocation();
useAutoNavigateOnboarding();
const onboardingStatus = useOnboardingStatus();
useEffect(() => {
const isMachinOngoingUserCreationRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify);
const isMatchingOnboardingRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify) ||
isMatchingLocation(AppPath.CreateWorkspace) ||
isMatchingLocation(AppPath.CreateProfile);
if (
onboardingStatus === OnboardingStatus.OngoingUserCreation &&
!isMachinOngoingUserCreationRoute
) {
navigate(AppPath.SignIn);
} else if (
onboardingStatus === OnboardingStatus.OngoingWorkspaceCreation &&
!isMatchingLocation(AppPath.CreateWorkspace)
) {
navigate(AppPath.CreateWorkspace);
} else if (
onboardingStatus === OnboardingStatus.OngoingProfileCreation &&
!isMatchingLocation(AppPath.CreateProfile)
) {
navigate(AppPath.CreateProfile);
} else if (
onboardingStatus === OnboardingStatus.Completed &&
isMatchingOnboardingRoute
) {
navigate('/');
}
}, [onboardingStatus, navigate, isMatchingLocation]);
return (
<StyledLayout>
<>
<CommandMenu />
<NavbarAnimatedContainer>
<AppNavbar />
</NavbarAnimatedContainer>
<MainContainer>
{onboardingStatus &&
onboardingStatus !== OnboardingStatus.Completed ? (
<>
<CompaniesMockMode />
<AnimatePresence mode="wait">
<LayoutGroup>
<AuthModal>{children}</AuthModal>
</LayoutGroup>
</AnimatePresence>
</>
) : (
<>{children}</>
)}
</MainContainer>
</>
<CommandMenu />
<NavbarAnimatedContainer>
<AppNavbar />
</NavbarAnimatedContainer>
<MainContainer>
{onboardingStatus && onboardingStatus !== OnboardingStatus.Completed ? (
<>
<CompaniesMockMode />
<AnimatePresence mode="wait">
<LayoutGroup>
<AuthModal>{children}</AuthModal>
</LayoutGroup>
</AnimatePresence>
</>
) : (
<>{children}</>
)}
</MainContainer>
</StyledLayout>
);
}

View File

@ -28,6 +28,7 @@ type LeftContainerProps = {
const StyledLeftContainer = styled.div<LeftContainerProps>`
display: flex;
flex-direction: column;
position: relative;
width: 100%;
`;

View File

@ -0,0 +1,11 @@
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
export const ShowPageContainer = styled.div`
display: flex;
flex-direction: ${() => (useIsMobile() ? 'column' : 'row')};
gap: ${({ theme }) => (useIsMobile() ? theme.spacing(3) : '0')};
height: 100%;
width: ${() => (useIsMobile() ? `calc(100% - 2px);` : '100%')};
`;

View File

@ -12,7 +12,7 @@ const StyledContainer = styled.div`
width: 100%;
`;
export function NoTopBarContainer({ children }: OwnProps) {
export function SubMenuTopBarContainer({ children }: OwnProps) {
return (
<StyledContainer>
<RightDrawerContainer topMargin={16}>{children}</RightDrawerContainer>

View File

@ -0,0 +1,52 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useIsMatchingLocation } from '../../../../hooks/useIsMatchingLocation';
import { useOnboardingStatus } from '../../../auth/hooks/useOnboardingStatus';
import { OnboardingStatus } from '../../../auth/utils/getOnboardingStatus';
import { AppPath } from '../../../types/AppPath';
export function useAutoNavigateOnboarding() {
const navigate = useNavigate();
const isMatchingLocation = useIsMatchingLocation();
const onboardingStatus = useOnboardingStatus();
useEffect(() => {
const isMachinOngoingUserCreationRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify);
const isMatchingOnboardingRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify) ||
isMatchingLocation(AppPath.CreateWorkspace) ||
isMatchingLocation(AppPath.CreateProfile);
if (
onboardingStatus === OnboardingStatus.OngoingUserCreation &&
!isMachinOngoingUserCreationRoute
) {
navigate(AppPath.SignIn);
} else if (
onboardingStatus === OnboardingStatus.OngoingWorkspaceCreation &&
!isMatchingLocation(AppPath.CreateWorkspace)
) {
navigate(AppPath.CreateWorkspace);
} else if (
onboardingStatus === OnboardingStatus.OngoingProfileCreation &&
!isMatchingLocation(AppPath.CreateProfile)
) {
navigate(AppPath.CreateProfile);
} else if (
onboardingStatus === OnboardingStatus.Completed &&
isMatchingOnboardingRoute
) {
navigate('/');
}
}, [onboardingStatus, navigate, isMatchingLocation]);
}

View File

@ -1,6 +1,6 @@
import { useLocation } from 'react-router-dom';
export function useIsSubNavbarDisplayed() {
export function useIsSubMenuNavbarDisplayed() {
const currentPath = useLocation().pathname;
return currentPath.match(/\/settings\//g) !== null;
}

View File

@ -1,13 +1,23 @@
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
export const ShowPageLeftContainer = styled.div`
background: ${({ theme }) => theme.background.secondary};
border-bottom-left-radius: 8px;
border-right: 1px solid ${({ theme }) => theme.border.color.medium};
border-right: 1px solid
${({ theme }) => {
const isMobile = useIsMobile();
return !isMobile ? theme.border.color.medium : 0;
}};
border-top-left-radius: 8px;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
padding: 0px ${({ theme }) => theme.spacing(3)};
width: 320px;
width: ${({ theme }) => {
const isMobile = useIsMobile();
return isMobile ? `calc(100% - ${theme.spacing(6)})` : '320px';
}};
`;

View File

@ -1,12 +1,16 @@
import { ReactNode, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { IconButton } from '@/ui/button/components/IconButton';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { IconChevronLeft, IconPlus } from '@/ui/icon/index';
import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton';
import { navbarIconSize } from '../../../navbar/constants';
import { OverflowingTextWithTooltip } from '../../../tooltip/OverflowingTextWithTooltip';
import { isNavbarOpenedState } from '../../states/isNavbarOpenedState';
export const TOP_BAR_MIN_HEIGHT = 40;
@ -20,6 +24,7 @@ const TopBarContainer = styled.div`
justify-content: space-between;
min-height: ${TOP_BAR_MIN_HEIGHT}px;
padding: ${({ theme }) => theme.spacing(2)};
padding-left: 0;
padding-right: ${({ theme }) => theme.spacing(3)};
`;
@ -37,10 +42,22 @@ const TitleContainer = styled.div`
max-width: 50%;
`;
const TopBarButtonContainer = styled.div`
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const BackIconButton = styled(IconButton)`
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const StyledTopBarIconTitleContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
padding-left: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
type OwnProps = {
title: string;
hasBackButton?: boolean;
@ -57,21 +74,38 @@ export function TopBar({
const navigate = useNavigate();
const navigateBack = useCallback(() => navigate(-1), [navigate]);
const isMobile = useIsMobile();
const isNavbarOpened = useRecoilValue(isNavbarOpenedState);
const showNavCollapseButton = isMobile || !isNavbarOpened;
const iconSize = useIsMobile()
? navbarIconSize.mobile
: navbarIconSize.desktop;
return (
<>
<TopBarContainer>
<StyledLeftContainer>
<NavCollapseButton hideIfOpen={true} />
{hasBackButton && (
<BackIconButton
icon={<IconChevronLeft size={16} />}
onClick={navigateBack}
/>
{showNavCollapseButton && (
<TopBarButtonContainer>
<NavCollapseButton direction="right" />
</TopBarButtonContainer>
)}
{icon}
<TitleContainer data-testid="top-bar-title">
<OverflowingTextWithTooltip text={title} />
</TitleContainer>
{hasBackButton && (
<TopBarButtonContainer>
<BackIconButton
icon={<IconChevronLeft size={iconSize} />}
onClick={navigateBack}
/>
</TopBarButtonContainer>
)}
<StyledTopBarIconTitleContainer>
{icon}
<TitleContainer data-testid="top-bar-title">
<OverflowingTextWithTooltip text={title} />
</TitleContainer>
</StyledTopBarIconTitleContainer>
</StyledLeftContainer>
{onAddButtonClick && (
<IconButton

View File

@ -1,7 +1,5 @@
import styled from '@emotion/styled';
import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton';
const TitleAndCollapseContainer = styled.div`
align-items: center;
display: flex;
@ -22,7 +20,6 @@ type OwnProps = {
export function TopTitle({ title }: OwnProps) {
return (
<TitleAndCollapseContainer>
<NavCollapseButton hideIfOpen={true} hideOnDesktop={true} />
<TitleContainer data-testid="top-bar-title">{title}</TitleContainer>
</TitleAndCollapseContainer>
);