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:
@@ -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 +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 };
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
Reference in New Issue
Block a user