ISSUE Closes https://github.com/twentyhq/twenty/issues/7085 DEMO https://github.com/user-attachments/assets/39692906-c02e-4e4c-9205-82447fa142df --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||||
|
import { useOpenSettingsMenu } from '@/navigation/hooks/useOpenSettings';
|
||||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import {
|
import {
|
||||||
@ -23,6 +24,8 @@ export const MobileNavigationBar = () => {
|
|||||||
const [currentMobileNavigationDrawer, setCurrentMobileNavigationDrawer] =
|
const [currentMobileNavigationDrawer, setCurrentMobileNavigationDrawer] =
|
||||||
useRecoilState(currentMobileNavigationDrawerState);
|
useRecoilState(currentMobileNavigationDrawerState);
|
||||||
|
|
||||||
|
const { openSettingsMenu } = useOpenSettingsMenu();
|
||||||
|
|
||||||
const activeItemName = isNavigationDrawerExpanded
|
const activeItemName = isNavigationDrawerExpanded
|
||||||
? currentMobileNavigationDrawer
|
? currentMobileNavigationDrawer
|
||||||
: isCommandMenuOpened
|
: isCommandMenuOpened
|
||||||
@ -62,10 +65,7 @@ export const MobileNavigationBar = () => {
|
|||||||
Icon: IconSettings,
|
Icon: IconSettings,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
closeCommandMenu();
|
closeCommandMenu();
|
||||||
setIsNavigationDrawerExpanded(
|
openSettingsMenu();
|
||||||
(previousIsOpen) => activeItemName !== 'settings' || !previousIsOpen,
|
|
||||||
);
|
|
||||||
setCurrentMobileNavigationDrawer('settings');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { currentMobileNavigationDrawerState } from '@/navigation/states/currentMobileNavigationDrawerState';
|
||||||
|
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
export const useOpenSettingsMenu = () => {
|
||||||
|
const [, setIsNavigationDrawerExpanded] = useRecoilState(
|
||||||
|
isNavigationDrawerExpandedState,
|
||||||
|
);
|
||||||
|
const [, setCurrentMobileNavigationDrawer] = useRecoilState(
|
||||||
|
currentMobileNavigationDrawerState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openSettingsMenu = () => {
|
||||||
|
setIsNavigationDrawerExpanded(true);
|
||||||
|
setCurrentMobileNavigationDrawer('settings');
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openSettingsMenu };
|
||||||
|
};
|
||||||
@ -4,9 +4,10 @@ import { SettingsIntegrationDatabaseTablesListCard } from '@/settings/integratio
|
|||||||
import { useDatabaseConnection } from '@/settings/integrations/database-connection/hooks/useDatabaseConnection';
|
import { useDatabaseConnection } from '@/settings/integrations/database-connection/hooks/useDatabaseConnection';
|
||||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
|
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||||
import { Section } from '@react-email/components';
|
import { Section } from '@react-email/components';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Breadcrumb, H2Title } from 'twenty-ui';
|
import { H2Title } from 'twenty-ui';
|
||||||
|
|
||||||
export const SettingsIntegrationDatabaseConnectionShowContainer = () => {
|
export const SettingsIntegrationDatabaseConnectionShowContainer = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|||||||
@ -12,12 +12,13 @@ import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
|||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
|
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { Section } from '@react-email/components';
|
import { Section } from '@react-email/components';
|
||||||
import pick from 'lodash.pick';
|
import pick from 'lodash.pick';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Breadcrumb, H2Title, Info } from 'twenty-ui';
|
import { H2Title, Info } from 'twenty-ui';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import {
|
import {
|
||||||
RemoteServer,
|
RemoteServer,
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { InformationBannerWrapper } from '@/information-banner/components/InformationBannerWrapper';
|
import { InformationBannerWrapper } from '@/information-banner/components/InformationBannerWrapper';
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbProps,
|
||||||
|
} from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { JSX, ReactNode } from 'react';
|
import { JSX, ReactNode } from 'react';
|
||||||
import { Breadcrumb, BreadcrumbProps } from 'twenty-ui';
|
|
||||||
import { PageBody } from './PageBody';
|
import { PageBody } from './PageBody';
|
||||||
import { PageHeader } from './PageHeader';
|
import { PageHeader } from './PageHeader';
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { MobileBreadcrumb } from '@/ui/navigation/bread-crumb/components/MobileBreadcrumb';
|
||||||
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Fragment, ReactNode } from 'react';
|
import { Fragment, ReactNode } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@ -39,6 +41,12 @@ const StyledDivider = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const Breadcrumb = ({ className, links }: BreadcrumbProps) => {
|
export const Breadcrumb = ({ className, links }: BreadcrumbProps) => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
if (isMobile && links.length > 0) {
|
||||||
|
return <MobileBreadcrumb className={className} links={links} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className={className}>
|
<StyledWrapper className={className}>
|
||||||
{links.map((link, index) => {
|
{links.map((link, index) => {
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import { useOpenSettingsMenu } from '@/navigation/hooks/useOpenSettings';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { IconChevronLeft } from 'twenty-ui';
|
||||||
|
|
||||||
|
export type MobileBreadcrumbProps = {
|
||||||
|
className?: string;
|
||||||
|
links: { children: string | ReactNode; href?: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledWrapper = styled.nav`
|
||||||
|
align-items: center;
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
display: grid;
|
||||||
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-column-gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
height: ${({ theme }) => theme.spacing(8)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledLink = styled(Link)`
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledText = styled.span`
|
||||||
|
color: inherit;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MobileBreadcrumb = ({
|
||||||
|
className,
|
||||||
|
links,
|
||||||
|
}: MobileBreadcrumbProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const { openSettingsMenu } = useOpenSettingsMenu();
|
||||||
|
|
||||||
|
const handleBackToSettingsClick = () => {
|
||||||
|
openSettingsMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
const previousLink = links[links.length - 2];
|
||||||
|
const shouldRedirectToSettings = links.length === 2;
|
||||||
|
|
||||||
|
const text = isNonEmptyString(previousLink.children)
|
||||||
|
? previousLink.children
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledWrapper className={className}>
|
||||||
|
{shouldRedirectToSettings ? (
|
||||||
|
<>
|
||||||
|
<IconChevronLeft size={theme.icon.size.md} />
|
||||||
|
<StyledText onClick={handleBackToSettingsClick}>
|
||||||
|
Back to Settings
|
||||||
|
</StyledText>
|
||||||
|
</>
|
||||||
|
) : previousLink?.href ? (
|
||||||
|
<>
|
||||||
|
<IconChevronLeft size={theme.icon.size.md} />
|
||||||
|
<StyledLink title={text} to={previousLink.href}>
|
||||||
|
Back to {previousLink.children}
|
||||||
|
</StyledLink>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<StyledText title={text}>{previousLink?.children}</StyledText>
|
||||||
|
)}
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
import { ComponentDecorator, Breadcrumb } from 'twenty-ui';
|
import { ComponentDecorator } from 'twenty-ui';
|
||||||
|
import { Breadcrumb } from '../Breadcrumb';
|
||||||
|
|
||||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
export * from './breadcrumb/components/Breadcrumb';
|
|
||||||
export * from './link/components/ActionLink';
|
export * from './link/components/ActionLink';
|
||||||
export * from './link/components/AdvancedSettingsToggle';
|
export * from './link/components/AdvancedSettingsToggle';
|
||||||
export * from './link/components/ContactLink';
|
export * from './link/components/ContactLink';
|
||||||
|
|||||||
Reference in New Issue
Block a user