feat: improve mobile display by tab bar and other changes (#2304)

* feat: improve mobile display by tab bar and other changes

* fix: remove unused declaration in mobile navigation

* fix: update desktop navbar stories title

* fix: retrieve old titles for desktop-navbar stories

* fix: styles, manage active tabs

* fix: styles, manage active tabs

* fix: styles, manage active tabs

* fix: styles, manage active tabs

* fix: styles, manage active tabs

* fix: styles, manage active tabs

* fix: styles, manage active tabs

* fix: styles, manage active tabs

* fix: update logic for tab bar menu icons

* fix: remove Settings icon for mobile

* fix: resolve comments in pl

* feat: rework mobile navigation bar

* Fix

* Fixes

---------

Co-authored-by: Thaïs Guigon <guigon.thais@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Saba Shavidze
2023-12-02 02:16:34 +04:00
committed by GitHub
parent 74b077f3ca
commit fec8223ab8
50 changed files with 640 additions and 380 deletions
@@ -0,0 +1,6 @@
import { useLocation } from 'react-router-dom';
export const useIsMenuNavbarDisplayed = () => {
const currentPath = useLocation().pathname;
return currentPath.match(/^\/companies(\/.*)?$/) !== null;
};
@@ -1,6 +0,0 @@
import { useLocation } from 'react-router-dom';
export const useIsSubMenuNavbarDisplayed = () => {
const currentPath = useLocation().pathname;
return currentPath.match(/\/settings\//g) !== null;
};
@@ -1,6 +1,6 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { AnimatePresence, LayoutGroup } from 'framer-motion';
import { useRecoilValue } from 'recoil';
import { AuthModal } from '@/auth/components/Modal';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
@@ -8,23 +8,22 @@ import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { CommandMenu } from '@/command-menu/components/CommandMenu';
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
import { KeyboardShortcutMenu } from '@/keyboard-shortcut-menu/components/KeyboardShortcutMenu';
import { DesktopNavigationDrawer } from '@/navigation/components/DesktopNavigationDrawer';
import { MobileNavigationBar } from '@/navigation/components/MobileNavigationBar';
import { MobileNavigationDrawer } from '@/navigation/components/MobileNavigationDrawer';
import { SignInBackgroundMockPage } from '@/sign-in-background-mock/components/SignInBackgroundMockPage';
import { NavbarAnimatedContainer } from '@/ui/navigation/navbar/components/NavbarAnimatedContainer';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
import { AppNavbar } from '~/AppNavbar';
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
import { NavbarAnimatedContainer } from '@/ui/navigation/navigation-drawer/components/NavbarAnimatedContainer';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
const StyledLayout = styled.div`
background: ${({ theme }) => theme.background.noisy};
display: flex;
flex-direction: row;
flex-direction: column;
height: 100vh;
position: relative;
scrollbar-color: ${({ theme }) => theme.border.color.medium};
scrollbar-width: 4px;
width: 100vw;
width: 100%;
*::-webkit-scrollbar {
height: 4px;
@@ -41,43 +40,51 @@ const StyledLayout = styled.div`
}
`;
const StyledMainContainer = styled.div`
const StyledPageContainer = styled.div`
display: flex;
flex: 1;
flex-direction: row;
`;
const StyledMainContainer = styled.div`
display: flex;
flex: 0 1 100%;
flex-direction: row;
overflow: hidden;
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: ${() => (useRecoilValue(isNavbarOpenedState) ? '0' : '100%')};
}
`;
type DefaultLayoutProps = {
children: React.ReactNode;
children: ReactNode;
};
export const DefaultLayout = ({ children }: DefaultLayoutProps) => {
const onboardingStatus = useOnboardingStatus();
const isMobile = useIsMobile();
return (
<StyledLayout>
<CommandMenu />
<KeyboardShortcutMenu />
<NavbarAnimatedContainer>
<AppNavbar />
</NavbarAnimatedContainer>
<StyledMainContainer>
{onboardingStatus && onboardingStatus !== OnboardingStatus.Completed ? (
<>
<SignInBackgroundMockPage />
<AnimatePresence mode="wait">
<LayoutGroup>
<AuthModal>{children}</AuthModal>
</LayoutGroup>
</AnimatePresence>
</>
) : (
<AppErrorBoundary>{children}</AppErrorBoundary>
)}
</StyledMainContainer>
<StyledPageContainer>
<NavbarAnimatedContainer>
{isMobile ? <MobileNavigationDrawer /> : <DesktopNavigationDrawer />}
</NavbarAnimatedContainer>
<StyledMainContainer>
{onboardingStatus &&
onboardingStatus !== OnboardingStatus.Completed ? (
<>
<SignInBackgroundMockPage />
<AnimatePresence mode="wait">
<LayoutGroup>
<AuthModal>{children}</AuthModal>
</LayoutGroup>
</AnimatePresence>
</>
) : (
<AppErrorBoundary>{children}</AppErrorBoundary>
)}
</StyledMainContainer>
</StyledPageContainer>
{isMobile && <MobileNavigationBar />}
</StyledLayout>
);
};
+1 -12
View File
@@ -1,12 +1 @@
import { PAGE_BAR_MIN_HEIGHT } from './PageHeader';
import { RightDrawerContainer } from './RightDrawerContainer';
type PageBodyProps = {
children: JSX.Element | JSX.Element[];
};
export const PageBody = ({ children }: PageBodyProps) => (
<RightDrawerContainer topMargin={PAGE_BAR_MIN_HEIGHT + 16 + 16}>
{children}
</RightDrawerContainer>
);
export { RightDrawerContainer as PageBody } from './RightDrawerContainer';
@@ -1,15 +1,9 @@
import styled from '@emotion/styled';
type PageContainerProps = {
children: JSX.Element | JSX.Element[];
};
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
`;
export const PageContainer = ({ children }: PageContainerProps) => (
<StyledContainer>{children}</StyledContainer>
);
export { StyledContainer as PageContainer };
+33 -35
View File
@@ -1,4 +1,4 @@
import { ComponentProps, useCallback } from 'react';
import { ComponentProps, ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
@@ -7,15 +7,12 @@ import { useRecoilValue } from 'recoil';
import { IconChevronLeft } from '@/ui/display/icon/index';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip';
import {
IconButton,
IconButtonSize,
} from '@/ui/input/button/components/IconButton';
import NavCollapseButton from '@/ui/navigation/navbar/components/NavCollapseButton';
import { IconButton } from '@/ui/input/button/components/IconButton';
import NavCollapseButton from '@/ui/navigation/navigation-drawer/components/NavCollapseButton';
import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
export const PAGE_BAR_MIN_HEIGHT = 40;
const StyledTopBarContainer = styled.div`
@@ -31,13 +28,23 @@ const StyledTopBarContainer = styled.div`
padding-left: 0;
padding-right: ${({ theme }) => theme.spacing(3)};
z-index: 20;
@media (max-width: ${MOBILE_VIEWPORT}px) {
padding-left: ${({ theme }) => theme.spacing(3)};
}
`;
const StyledLeftContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(2)};
width: 100%;
@media (max-width: ${MOBILE_VIEWPORT}px) {
padding-left: ${({ theme }) => theme.spacing(1)};
}
`;
const StyledTitleContainer = styled.div`
@@ -47,24 +54,15 @@ const StyledTitleContainer = styled.div`
max-width: 50%;
`;
const StyledTopBarButtonContainer = styled.div`
margin-left: ${({ theme }) => theme.spacing(1)};
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const StyledBackIconButton = styled(IconButton)`
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const StyledTopBarIconStyledTitleContainer = styled.div<{
hideLeftPadding?: boolean;
}>`
const StyledTopBarIconStyledTitleContainer = styled.div`
align-items: center;
display: flex;
flex: 1 0 100%;
flex-direction: row;
padding-left: ${({ theme, hideLeftPadding }) =>
hideLeftPadding ? theme.spacing(2) : undefined};
width: 100%;
`;
const StyledPageActionContainer = styled.div`
@@ -72,11 +70,16 @@ const StyledPageActionContainer = styled.div`
gap: ${({ theme }) => theme.spacing(2)};
`;
const StyledTopBarButtonContainer = styled.div`
margin-left: ${({ theme }) => theme.spacing(1)};
margin-right: ${({ theme }) => theme.spacing(1)};
`;
type PageHeaderProps = ComponentProps<'div'> & {
title: string;
hasBackButton?: boolean;
Icon: IconComponent;
children?: JSX.Element | JSX.Element[];
children?: ReactNode;
};
export const PageHeader = ({
@@ -85,33 +88,28 @@ export const PageHeader = ({
Icon,
children,
}: PageHeaderProps) => {
const isMobile = useIsMobile();
const navigate = useNavigate();
const navigateBack = useCallback(() => navigate(-1), [navigate]);
const isNavbarOpened = useRecoilValue(isNavbarOpenedState);
const iconSize: IconButtonSize = useIsMobile() ? 'small' : 'medium';
const theme = useTheme();
const navigationDrawer = useRecoilValue(navigationDrawerState);
return (
<StyledTopBarContainer>
<StyledLeftContainer>
{!isNavbarOpened && (
{navigationDrawer === '' && (
<StyledTopBarButtonContainer>
<NavCollapseButton direction="right" />
</StyledTopBarButtonContainer>
)}
{hasBackButton && (
<StyledTopBarButtonContainer>
<StyledBackIconButton
Icon={IconChevronLeft}
size={iconSize}
onClick={navigateBack}
variant="tertiary"
/>
</StyledTopBarButtonContainer>
<StyledBackIconButton
Icon={IconChevronLeft}
size={isMobile ? 'small' : 'medium'}
onClick={() => navigate(-1)}
variant="tertiary"
/>
)}
<StyledTopBarIconStyledTitleContainer hideLeftPadding={!hasBackButton}>
<StyledTopBarIconStyledTitleContainer>
{Icon && <Icon size={theme.icon.size.md} />}
<StyledTitleContainer data-testid="top-bar-title">
<OverflowingTextWithTooltip text={title} />
@@ -1,25 +1,30 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { RightDrawer } from '@/ui/layout/right-drawer/components/RightDrawer';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
import { PagePanel } from './PagePanel';
type RightDrawerContainerProps = {
children: JSX.Element | JSX.Element[];
topMargin?: number;
children: ReactNode;
};
const StyledMainContainer = styled.div<{ topMargin: number }>`
const StyledMainContainer = styled.div`
background: ${({ theme }) => theme.background.noisy};
box-sizing: border-box;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
height: calc(100% - ${(props) => props.topMargin}px);
height: 100%;
padding-bottom: ${({ theme }) => theme.spacing(3)};
padding-right: ${({ theme }) => theme.spacing(3)};
width: calc(100% - ${({ theme }) => theme.spacing(3)});
width: 100%;
@media (max-width: ${MOBILE_VIEWPORT}px) {
padding-left: ${({ theme }) => theme.spacing(3)};
padding-bottom: 0;
}
`;
type LeftContainerProps = {
@@ -35,9 +40,8 @@ const StyledLeftContainer = styled.div<LeftContainerProps>`
export const RightDrawerContainer = ({
children,
topMargin,
}: RightDrawerContainerProps) => (
<StyledMainContainer topMargin={topMargin ?? 0}>
<StyledMainContainer>
<StyledLeftContainer>
<PagePanel>{children}</PagePanel>
</StyledLeftContainer>
@@ -30,7 +30,7 @@ export const SubMenuTopBarContainer = ({
return (
<StyledContainer isMobile={isMobile}>
{isMobile && <PageHeader title={title} Icon={Icon} />}
<RightDrawerContainer topMargin={16}>{children}</RightDrawerContainer>
<RightDrawerContainer>{children}</RightDrawerContainer>
</StyledContainer>
);
};
@@ -13,7 +13,6 @@ import {
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { isDefined } from '~/utils/isDefined';
import { leftNavbarWidth } from '../../../navigation/navbar/constants';
import { useRightDrawer } from '../hooks/useRightDrawer';
import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
@@ -70,15 +69,9 @@ export const RightDrawer = () => {
const isMobile = useIsMobile();
const rightDrawerWidthExpanded = `calc(100% - ${
leftNavbarWidth.desktop
} - ${theme.spacing(2)})`;
const rightDrawerWidth = isRightDrawerOpen
? isMobile
? isMobile || isRightDrawerExpanded
? '100%'
: isRightDrawerExpanded
? rightDrawerWidthExpanded
: theme.rightDrawerWidth
: '0';
@@ -1,10 +0,0 @@
import { atom } from 'recoil';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
const isMobile = window.innerWidth <= MOBILE_VIEWPORT;
export const isNavbarOpenedState = atom({
key: 'ui/isNavbarOpenedState',
default: !isMobile,
});
@@ -1,6 +0,0 @@
import { atom } from 'recoil';
export const isNavbarSwitchingSizeState = atom({
key: 'ui/isNavbarSwitchingSizeState',
default: true,
});