Skip to content

Commit

Permalink
Created a breadcrumb for left nav menu sub items (#6762)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasbordeau authored Aug 30, 2024
1 parent 09ac8e3 commit 26eba76
Show file tree
Hide file tree
Showing 16 changed files with 450 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -41,8 +41,8 @@ export const MainNavigationDrawerItems = () => {

<Favorites />

<ObjectMetadataNavItems isRemote={false} />
<ObjectMetadataNavItems isRemote={true} />
<NavigationDrawerSectionForObjectMetadataItems isRemote={false} />
<NavigationDrawerSectionForObjectMetadataItems isRemote={true} />
</>
);
};
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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',
Expand All @@ -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 } =
Expand All @@ -57,13 +47,13 @@ export const ObjectMetadataNavItems = ({ isRemote }: { isRemote: boolean }) => {
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
const loading = useIsPrefetchLoading();

const theme = useTheme();
const { getLastVisitedViewIdFromObjectMetadataItemId } = useLastVisitedView();

if (loading && isDefined(currentUser)) {
return <ObjectMetadataNavItemsSkeletonLoader />;
return <NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader />;
}

// TODO: refactor this by splitting into separate components
return (
filteredActiveObjectMetadataItems.length > 0 && (
<NavigationDrawerSection>
Expand Down Expand Up @@ -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 (
<div key={objectMetadataItem.id}>
<NavigationDrawerItem
Expand All @@ -132,33 +133,21 @@ export const ObjectMetadataNavItems = ({ isRemote }: { isRemote: boolean }) => {
currentPath === `/objects/${objectMetadataItem.namePlural}`
}
/>
<AnimatePresence>
{shouldSubItemsBeDisplayed && (
<motion.div
initial="hidden"
animate="visible"
exit="hidden"
variants={navItemsAnimationVariants(theme)}
transition={{ duration: 0.3, ease: 'easeInOut' }}
>
{objectMetadataViews
.sort((viewA, viewB) =>
viewA.key === 'INDEX'
? -1
: viewA.position - viewB.position,
)
.map((view) => (
<NavigationDrawerSubItem
label={view.name}
to={`/objects/${objectMetadataItem.namePlural}?view=${view.id}`}
active={viewId === view.id}
Icon={getIcon(view.icon)}
key={view.id}
/>
))}
</motion.div>
)}
</AnimatePresence>
{shouldSubItemsBeDisplayed &&
sortedObjectMetadataViews.map((view, index) => (
<NavigationDrawerSubItem
label={view.name}
to={`/objects/${objectMetadataItem.namePlural}?view=${view.id}`}
active={viewId === view.id}
subItemState={getNavigationSubItemState({
index,
arrayLength: subItemArrayLength,
selectedIndex: selectedSubItemIndex,
})}
Icon={getIcon(view.icon)}
key={view.id}
/>
))}
</div>
);
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.light}
borderRadius={4}
>
<StyledSkeletonColumn>
<Skeleton width={196} height={16} />
<Skeleton width={196} height={16} />
<Skeleton width={196} height={16} />
</StyledSkeletonColumn>
</SkeletonTheme>
);
};

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof ObjectMetadataNavItems> = {
title: 'Modules/ObjectMetadata/ObjectMetadataNavItems',
component: ObjectMetadataNavItems,
const meta: Meta<typeof NavigationDrawerSectionForObjectMetadataItems> = {
title: 'Modules/ObjectMetadata/NavigationDrawerSectionForObjectMetadataItems',
component: NavigationDrawerSectionForObjectMetadataItems,
decorators: [
IconsProviderDecorator,
ObjectMetadataItemsDecorator,
Expand All @@ -29,7 +29,7 @@ const meta: Meta<typeof ObjectMetadataNavItems> = {
};

export default meta;
type Story = StoryObj<typeof ObjectMetadataNavItems>;
type Story = StoryObj<typeof NavigationDrawerSectionForObjectMetadataItems>;

export const Default: Story = {
play: async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const useHandleToggleTrashColumnFilter = ({
upsertCombinedViewFilter(newFilter);
}, [
columnDefinitions,
objectMetadataItem.fields,
objectMetadataItem,
objectNameSingular,
upsertCombinedViewFilter,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<NavigationDrawerItem
level={level}
indentationLevel={indentationLevel}
subItemState={subItemState}
label={label}
to={href}
Icon={Icon}
Expand Down
Loading

0 comments on commit 26eba76

Please sign in to comment.