diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx
index e28eb6bf83f4..367b121534d8 100644
--- a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx
+++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx
@@ -4,7 +4,7 @@ import { IconSearch, IconSettings } from 'twenty-ui';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { Favorites } from '@/favorites/components/Favorites';
-import { ObjectMetadataNavItems } from '@/object-metadata/components/ObjectMetadataNavItems';
+import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
@@ -41,8 +41,8 @@ export const MainNavigationDrawerItems = () => {
-
-
+
+
>
);
};
diff --git a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems.tsx
similarity index 73%
rename from packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx
rename to packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems.tsx
index d65eb003b804..b2cffdd7b5f3 100644
--- a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx
+++ b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems.tsx
@@ -1,11 +1,10 @@
-import { AnimatePresence, motion } from 'framer-motion';
import { useLocation } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { isDefined, useIcons } from 'twenty-ui';
import { currentUserState } from '@/auth/states/currentUserState';
import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
-import { ObjectMetadataNavItemsSkeletonLoader } from '@/object-metadata/components/ObjectMetadataNavItemsSkeletonLoader';
+import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
@@ -15,9 +14,9 @@ import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/compo
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
+import { getNavigationSubItemState } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemState';
import { View } from '@/views/types/View';
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
-import { Theme, useTheme } from '@emotion/react';
const ORDERED_STANDARD_OBJECTS = [
'person',
@@ -27,20 +26,11 @@ const ORDERED_STANDARD_OBJECTS = [
'note',
];
-const navItemsAnimationVariants = (theme: Theme) => ({
- hidden: {
- height: 0,
- opacity: 0,
- marginTop: 0,
- },
- visible: {
- height: 'auto',
- opacity: 1,
- marginTop: theme.spacing(1),
- },
-});
-
-export const ObjectMetadataNavItems = ({ isRemote }: { isRemote: boolean }) => {
+export const NavigationDrawerSectionForObjectMetadataItems = ({
+ isRemote,
+}: {
+ isRemote: boolean;
+}) => {
const currentUser = useRecoilValue(currentUserState);
const { toggleNavigationSection, isNavigationSectionOpenState } =
@@ -57,13 +47,13 @@ export const ObjectMetadataNavItems = ({ isRemote }: { isRemote: boolean }) => {
const { records: views } = usePrefetchedData(PrefetchKey.AllViews);
const loading = useIsPrefetchLoading();
- const theme = useTheme();
const { getLastVisitedViewIdFromObjectMetadataItemId } = useLastVisitedView();
if (loading && isDefined(currentUser)) {
- return ;
+ return ;
}
+ // TODO: refactor this by splitting into separate components
return (
filteredActiveObjectMetadataItems.length > 0 && (
@@ -121,6 +111,17 @@ export const ObjectMetadataNavItems = ({ isRemote }: { isRemote: boolean }) => {
currentPath === `/objects/${objectMetadataItem.namePlural}` &&
objectMetadataViews.length > 1;
+ const sortedObjectMetadataViews = [...objectMetadataViews].sort(
+ (viewA, viewB) =>
+ viewA.key === 'INDEX' ? -1 : viewA.position - viewB.position,
+ );
+
+ const selectedSubItemIndex = sortedObjectMetadataViews.findIndex(
+ (view) => viewId === view.id,
+ );
+
+ const subItemArrayLength = sortedObjectMetadataViews.length;
+
return (
{
currentPath === `/objects/${objectMetadataItem.namePlural}`
}
/>
-
- {shouldSubItemsBeDisplayed && (
-
- {objectMetadataViews
- .sort((viewA, viewB) =>
- viewA.key === 'INDEX'
- ? -1
- : viewA.position - viewB.position,
- )
- .map((view) => (
-
- ))}
-
- )}
-
+ {shouldSubItemsBeDisplayed &&
+ sortedObjectMetadataViews.map((view, index) => (
+
+ ))}
);
})}
diff --git a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader.tsx b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader.tsx
new file mode 100644
index 000000000000..70e00d717330
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader.tsx
@@ -0,0 +1,29 @@
+import { useTheme } from '@emotion/react';
+import styled from '@emotion/styled';
+import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
+
+const StyledSkeletonColumn = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(1)};
+ height: 76px;
+ padding-left: ${({ theme }) => theme.spacing(1)};
+`;
+
+export const NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader: React.FC =
+ () => {
+ const theme = useTheme();
+ return (
+
+
+
+
+
+
+
+ );
+ };
diff --git a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataNavItemsSkeletonLoader.tsx b/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataNavItemsSkeletonLoader.tsx
deleted file mode 100644
index 82669d907204..000000000000
--- a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataNavItemsSkeletonLoader.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
-import { useTheme } from '@emotion/react';
-import styled from '@emotion/styled';
-
-const StyledSkeletonColumn = styled.div`
- display: flex;
- flex-direction: column;
- gap: ${({ theme }) => theme.spacing(1)};
- height: 76px;
- padding-left: ${({ theme }) => theme.spacing(1)};
-`;
-
-export const ObjectMetadataNavItemsSkeletonLoader: React.FC = () => {
- const theme = useTheme();
- return (
-
-
-
-
-
-
-
- );
-};
diff --git a/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx b/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx
index 7e74871a5fee..bb5f7e982580 100644
--- a/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx
+++ b/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx
@@ -9,12 +9,12 @@ import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadat
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
+import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
import { PrefetchLoadedDecorator } from '~/testing/decorators/PrefetchLoadedDecorator';
-import { ObjectMetadataNavItems } from '../ObjectMetadataNavItems';
-const meta: Meta = {
- title: 'Modules/ObjectMetadata/ObjectMetadataNavItems',
- component: ObjectMetadataNavItems,
+const meta: Meta = {
+ title: 'Modules/ObjectMetadata/NavigationDrawerSectionForObjectMetadataItems',
+ component: NavigationDrawerSectionForObjectMetadataItems,
decorators: [
IconsProviderDecorator,
ObjectMetadataItemsDecorator,
@@ -29,7 +29,7 @@ const meta: Meta = {
};
export default meta;
-type Story = StoryObj;
+type Story = StoryObj;
export const Default: Story = {
play: async () => {
diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts
index 9e4a5c0c7e9e..b32a29f9aabb 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts
@@ -63,7 +63,7 @@ export const useHandleToggleTrashColumnFilter = ({
upsertCombinedViewFilter(newFilter);
}, [
columnDefinitions,
- objectMetadataItem.fields,
+ objectMetadataItem,
objectNameSingular,
upsertCombinedViewFilter,
]);
diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx
index 60733c0b60f9..050ca38f9e9b 100644
--- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx
+++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx
@@ -6,32 +6,38 @@ import {
NavigationDrawerItem,
NavigationDrawerItemProps,
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
+import { NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState';
type SettingsNavigationDrawerItemProps = Pick<
NavigationDrawerItemProps,
- 'Icon' | 'label' | 'level' | 'soon'
+ 'Icon' | 'label' | 'indentationLevel' | 'soon'
> & {
matchSubPages?: boolean;
path: SettingsPath;
+ subItemState?: NavigationDrawerSubItemState;
};
export const SettingsNavigationDrawerItem = ({
Icon,
label,
- level,
+ indentationLevel,
matchSubPages = false,
path,
soon,
+ subItemState,
}: SettingsNavigationDrawerItemProps) => {
const href = getSettingsPagePath(path);
+ const pathName = useResolvedPath(href).pathname;
+
const isActive = !!useMatch({
- path: useResolvedPath(href).pathname,
+ path: pathName,
end: !matchSubPages,
});
return (
{
const { signOut } = useAuth();
@@ -38,6 +53,39 @@ export const SettingsNavigationDrawerItems = () => {
const isBillingPageEnabled =
billing?.isBillingEnabled && !isFreeAccessEnabled;
+ // TODO: Refactor this part to only have arrays of navigation items
+ const currentPathName = useLocation().pathname;
+
+ const accountSubSettings: SettingsNavigationItem[] = [
+ {
+ label: 'Emails',
+ path: SettingsPath.AccountsEmails,
+ Icon: IconMail,
+ matchSubPages: true,
+ indentationLevel: 2,
+ },
+ {
+ label: 'Calendars',
+ path: SettingsPath.AccountsCalendars,
+ Icon: IconCalendarEvent,
+ matchSubPages: true,
+ indentationLevel: 2,
+ },
+ ];
+
+ const selectedIndex = accountSubSettings.findIndex((accountSubSetting) => {
+ const href = getSettingsPagePath(accountSubSetting.path);
+ const pathName = resolvePath(href).pathname;
+
+ return matchPath(
+ {
+ path: pathName,
+ end: !accountSubSetting.matchSubPages,
+ },
+ currentPathName,
+ );
+ });
+
return (
<>
@@ -58,23 +106,21 @@ export const SettingsNavigationDrawerItems = () => {
path={SettingsPath.Accounts}
Icon={IconAt}
/>
-
-
+ {accountSubSettings.map((navigationItem, index) => (
+
+ ))}
-
{
/>
)}
-
void;
Icon: IconComponent;
@@ -23,13 +30,10 @@ export type NavigationDrawerItemProps = {
keyboard?: string[];
};
-type StyledItemProps = {
- active?: boolean;
- danger?: boolean;
- level: 1 | 2;
- soon?: boolean;
- to?: string;
-};
+type StyledItemProps = Pick<
+ NavigationDrawerItemProps,
+ 'active' | 'danger' | 'indentationLevel' | 'soon' | 'to'
+>;
const StyledItem = styled('div', {
shouldForwardProp: (prop) =>
@@ -59,13 +63,17 @@ const StyledItem = styled('div', {
font-family: 'Inter';
font-size: ${({ theme }) => theme.font.size.md};
gap: ${({ theme }) => theme.spacing(2)};
- margin-left: ${({ level, theme }) => theme.spacing((level - 1) * 4)};
+
padding-bottom: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(1)};
padding-right: ${({ theme }) => theme.spacing(1)};
padding-top: ${({ theme }) => theme.spacing(1)};
- pointer-events: ${(props) => (props.soon ? 'none' : 'auto')};
+ margin-top: ${({ indentationLevel }) =>
+ indentationLevel === 2 ? '2px' : '0'};
+
+ pointer-events: ${(props) => (props.soon ? 'none' : 'auto')};
+ width: 100%;
:hover {
background: ${({ theme }) => theme.background.transparent.light};
color: ${(props) =>
@@ -116,10 +124,16 @@ const StyledKeyBoardShortcut = styled.div`
visibility: hidden;
`;
+const StyledNavigationDrawerItemContainer = styled.div`
+ display: flex;
+ flex-grow: 1;
+ width: 100%;
+`;
+
export const NavigationDrawerItem = ({
className,
label,
- level = 1,
+ indentationLevel = DEFAULT_INDENTATION_LEVEL,
Icon,
to,
onClick,
@@ -128,6 +142,7 @@ export const NavigationDrawerItem = ({
soon,
count,
keyboard,
+ subItemState,
}: NavigationDrawerItemProps) => {
const theme = useTheme();
const isMobile = useIsMobile();
@@ -136,6 +151,8 @@ export const NavigationDrawerItem = ({
isNavigationDrawerOpenState,
);
+ const showBreadcrumb = indentationLevel === 2;
+
const handleItemClick = () => {
if (isMobile) {
setIsNavigationDrawerOpen(false);
@@ -152,26 +169,33 @@ export const NavigationDrawerItem = ({
};
return (
-
- {Icon && }
- {label}
- {soon && }
- {!!count && {count}}
- {keyboard && (
-
- {keyboard}
-
- )}
-
+
+
+ {showBreadcrumb && (
+
+ )}
+ {Icon && (
+
+ )}
+ {label}
+ {soon && }
+ {!!count && {count}}
+ {keyboard && (
+
+ {keyboard}
+
+ )}
+
+
);
};
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItemBreadcrumb.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItemBreadcrumb.tsx
new file mode 100644
index 000000000000..47e8bd120864
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItemBreadcrumb.tsx
@@ -0,0 +1,79 @@
+import { NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState';
+import styled from '@emotion/styled';
+
+export type NavigationDrawerItemBreadcrumbProps = {
+ state?: NavigationDrawerSubItemState;
+};
+
+const StyledNavigationDrawerItemBreadcrumbContainer = styled.div`
+ margin-left: 7.5px;
+
+ height: 28px;
+ width: 9px;
+`;
+
+const StyledGapVerticalLine = styled.div<{ darker: boolean }>`
+ background: ${({ theme, darker }) =>
+ darker ? theme.font.color.tertiary : theme.border.color.strong};
+
+ position: relative;
+ top: -2px;
+
+ height: 2px;
+ width: 1px;
+`;
+
+const StyledSecondaryFullVerticalBar = styled.div<{ darker: boolean }>`
+ background: ${({ theme, darker }) =>
+ darker ? theme.font.color.tertiary : theme.border.color.strong};
+
+ position: relative;
+ top: -17px;
+ height: 28px;
+ width: 1px;
+`;
+
+const StyledRoundedProtrusion = styled.div<{ darker: boolean }>`
+ position: relative;
+ top: -2px;
+
+ border-bottom-left-radius: 4px;
+
+ border: 1px solid
+ ${({ theme, darker }) =>
+ darker ? theme.font.color.tertiary : theme.border.color.strong};
+
+ ${({ darker }) => (darker ? 'z-index: 1;' : '')}
+
+ border-top: none;
+ border-right: none;
+ height: 14px;
+ width: 8px;
+`;
+
+export const NavigationDrawerItemBreadcrumb = ({
+ state,
+}: NavigationDrawerItemBreadcrumbProps) => {
+ const showVerticalBar =
+ state !== 'last-not-selected' && state !== 'last-selected';
+
+ const verticalBarShouldBeDarker = state === 'intermediate-before-selected';
+
+ const protrusionShouldBeDarker =
+ state === 'intermediate-selected' || state === 'last-selected';
+
+ const gapShouldBeDarker =
+ state === 'intermediate-before-selected' ||
+ state === 'intermediate-selected' ||
+ state === 'last-selected';
+
+ return (
+
+
+
+ {showVerticalBar && (
+
+ )}
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup.tsx
index 3df470edb4e3..dfb9b5f7c61a 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup.tsx
@@ -3,7 +3,6 @@ import styled from '@emotion/styled';
const StyledGroup = styled.div`
display: flex;
flex-direction: column;
- gap: ${({ theme }) => theme.spacing(0.5)};
`;
export { StyledGroup as NavigationDrawerItemGroup };
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx
index fad4149edbfd..4ef3f1c9654c 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx
@@ -2,21 +2,12 @@ import {
NavigationDrawerItem,
NavigationDrawerItemProps,
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
-import styled from '@emotion/styled';
-
-const StyledItem = styled.div`
- &:not(:last-child) {
- margin-bottom: ${({ theme }) => theme.spacing(0.5)};
- }
- margin-left: ${({ theme }) => theme.spacing(4)};
-`;
type NavigationDrawerSubItemProps = NavigationDrawerItemProps;
export const NavigationDrawerSubItem = ({
className,
label,
- level = 1,
Icon,
to,
onClick,
@@ -25,22 +16,22 @@ export const NavigationDrawerSubItem = ({
soon,
count,
keyboard,
+ subItemState,
}: NavigationDrawerSubItemProps) => {
return (
-
-
-
+
);
};
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx
index 96d16a5526fe..f92d778e3b3f 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx
@@ -23,6 +23,7 @@ import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersion
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
+import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
import { NavigationDrawer } from '../NavigationDrawer';
import { NavigationDrawerItem } from '../NavigationDrawerItem';
import { NavigationDrawerItemGroup } from '../NavigationDrawerItemGroup';
@@ -108,17 +109,17 @@ export const Submenu: Story = {
to={getSettingsPagePath(SettingsPath.Accounts)}
Icon={IconAt}
/>
-
-
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawerItem.stories.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawerItem.stories.tsx
index f541a91aa615..81a0ffe4660b 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawerItem.stories.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawerItem.stories.tsx
@@ -37,6 +37,115 @@ export const Default: Story = {
],
};
+export const Breadcrumb: Story = {
+ decorators: [
+ (Story) => (
+
+ Breadcrumb
+
+
+
+
+
+
+
+ ),
+ ComponentWithRouterDecorator,
+ ],
+};
+
+export const BreadcrumbCatalog: CatalogStory<
+ Story,
+ typeof NavigationDrawerItem
+> = {
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ CatalogDecorator,
+ MemoryRouterDecorator,
+ ],
+ args: {
+ indentationLevel: 2,
+ },
+ parameters: {
+ pseudo: { hover: ['.hover'] },
+ catalog: {
+ dimensions: [
+ {
+ name: 'subItemState',
+ values: [
+ 'Intermediate before selected',
+ 'Intermediate selected',
+ 'Intermediate after selected',
+ 'Last not selected',
+ 'Last selected',
+ ],
+ props: (state: string) => {
+ switch (state) {
+ case 'Intermediate before selected':
+ return { subItemState: 'intermediate-before-selected' };
+ case 'Intermediate selected':
+ return { subItemState: 'intermediate-selected' };
+ case 'Intermediate after selected':
+ return { subItemState: 'intermediate-after-selected' };
+ case 'Last not selected':
+ return { subItemState: 'last-not-selected' };
+ case 'Last selected':
+ return { subItemState: 'last-selected' };
+ default:
+ throw new Error(`Unknown state: ${state}`);
+ }
+ },
+ },
+ ],
+ },
+ },
+};
+
export const Catalog: CatalogStory = {
decorators: [
(Story) => (
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState.ts b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState.ts
new file mode 100644
index 000000000000..7de9e4ef5675
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState.ts
@@ -0,0 +1,6 @@
+export type NavigationDrawerSubItemState =
+ | 'intermediate-before-selected'
+ | 'intermediate-selected'
+ | 'intermediate-after-selected'
+ | 'last-selected'
+ | 'last-not-selected';
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/utils/getNavigationSubItemState.ts b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/utils/getNavigationSubItemState.ts
new file mode 100644
index 000000000000..dcb08f78ee13
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/utils/getNavigationSubItemState.ts
@@ -0,0 +1,35 @@
+import { NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState';
+
+export const getNavigationSubItemState = ({
+ index,
+ arrayLength,
+ selectedIndex,
+}: {
+ index: number;
+ arrayLength: number;
+ selectedIndex: number;
+}): NavigationDrawerSubItemState => {
+ const thereIsOnlyOneItem = arrayLength === 1;
+
+ const itsTheLastItem = index === arrayLength - 1;
+
+ const itsTheSelectedItem = index === selectedIndex;
+
+ const itsBeforeTheSelectedItem = index < selectedIndex;
+
+ if (thereIsOnlyOneItem || itsTheLastItem) {
+ if (itsTheSelectedItem) {
+ return 'last-selected';
+ } else {
+ return 'last-not-selected';
+ }
+ } else {
+ if (itsTheSelectedItem) {
+ return 'intermediate-selected';
+ } else if (itsBeforeTheSelectedItem) {
+ return 'intermediate-before-selected';
+ } else {
+ return 'intermediate-after-selected';
+ }
+ }
+};