Skip to content

Commit

Permalink
feat: Global breadcrumb (#2847)
Browse files Browse the repository at this point in the history
Resolves #2841

# Add WebUI NEO Breadcrumb Navigation Component

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/XqC2uNFuj0wg8I60sMUh/1d58e4b9-385a-45e5-b4be-492fc61f1213.png)

Adds a breadcrumb navigation component to the WebUI interface, providing users with clear path indication and navigation capabilities. The breadcrumb appears below the header and displays the current location hierarchy based on the route structure.

Key features:
- Displays current navigation path with clickable links
- Integrates with existing routing system
- Supports internationalization (i18n) for labels
- Maintains consistent styling with Ant Design theme tokens
- Preserves navigation history for better user orientation

To review:
1. Verify breadcrumb appears correctly on all pages
2. Check navigation links work as expected
3. Confirm proper i18n label rendering
4. Validate styling consistency with design system
  • Loading branch information
yomybaby committed Nov 20, 2024
1 parent 504b649 commit 17ea21a
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 27 deletions.
35 changes: 8 additions & 27 deletions react/src/components/MainLayout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ForceTOTPChecker from '../ForceTOTPChecker';
import NetworkStatusBanner from '../NetworkStatusBanner';
import PasswordChangeRequestAlert from '../PasswordChangeRequestAlert';
import { DRAWER_WIDTH } from '../WEBUINotificationDrawer';
import WebUIBreadcrumb from '../WebUIBreadcrumb';
import WebUIHeader from './WebUIHeader';
import WebUISider from './WebUISider';
import { App, Layout, theme } from 'antd';
Expand Down Expand Up @@ -177,33 +178,6 @@ function MainLayout() {
/>
</div>
</Suspense>
{/* <Flex direction="column"> */}

{/* TODO: Breadcrumb */}
{/* {location.pathname.split("/").length > 3 && (
<Breadcrumb
items={matches.map((match, index) => {
return {
key: match.id,
href:
_.last(matches) === match
? undefined
: // @ts-ignore
match?.handle?.altPath || match.pathname,
//@ts-ignore
title: match?.handle?.title,
onClick:
_.last(matches) === match
? undefined
: (e) => {
e.preventDefault();
// @ts-ignore
navigate(match?.handle?.altPath || match.pathname);
},
};
})}
/>
)} */}
<Suspense>
<PasswordChangeRequestAlert
showIcon
Expand All @@ -217,6 +191,13 @@ function MainLayout() {
<ForceTOTPChecker />
</Suspense>
<Suspense>
<WebUIBreadcrumb
style={{
marginBottom: token.marginMD,
marginLeft: token.paddingContentHorizontalLG * -1,
marginRight: token.paddingContentHorizontalLG * -1,
}}
/>
<Outlet />
</Suspense>
{/* To match paddig to 16 (2+14) */}
Expand Down
99 changes: 99 additions & 0 deletions react/src/components/WebUIBreadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Flex, { FlexProps } from './Flex';
import WebUILink from './WebUILink';
import { Breadcrumb, theme } from 'antd';
import _ from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useMatches } from 'react-router-dom';

interface WebUIBreadcrumbProps extends FlexProps {}
const WebUIBreadcrumb: React.FC<WebUIBreadcrumbProps> = (props) => {
// const location = useLocation();
const matches = useMatches();
// matches[0].handle.

const { token } = theme.useToken();

const { t } = useTranslation();
return (
<Flex
direction="column"
justify="center"
align="stretch"
{...props}
style={_.merge(
{
height: 40,
paddingLeft: token.paddingContentHorizontalLG,
borderBottom: `1px solid ${token.colorBorder}`,
} as React.CSSProperties,
props.style,
)}
>
<Breadcrumb
style={{
fontSize: token.fontSizeSM,
// lineHeight: he,
}}
items={[
..._.chain(matches)
.filter((match) => {
return (
// @ts-ignore
!_.isEmpty(match?.handle?.labelKey) ||
// @ts-ignore
!_.isEmpty(match?.handle?.title)
);
})
.map((match, index) => {
return {
key: match.id,
href:
_.last(matches) === match
? undefined
: // @ts-ignore
match?.handle?.altPath || match.pathname,
//@ts-ignore
title: match?.handle?.title || t(match?.handle?.labelKey),
// onClick:
// _.last(matches) === match
// ? undefined
// : (e: any) => {
// e.preventDefault();
// // @ts-ignore
// navigate(match?.handle?.altPath || match.pathname);
// },
};
})
.value(),
{
// Add dummy tail to add a `/` at the end of the breadcrumb
key: 'dummy_tail',
title: ' ',
},
]}
itemRender={(currentRoute, params, items, paths) => {
const isLast =
currentRoute?.key === items[items.length - 2]?.key ||
currentRoute?.key === 'dummy_tail';
return isLast || !currentRoute.href ? (
<span>{currentRoute.title}</span>
) : (
<WebUILink
to={currentRoute.href}
style={{
margin: 0,
padding: 0,
height: 'unset'
}}
>
{currentRoute.title}
</WebUILink>
);
}}
/>
</Flex>
);
};

export default WebUIBreadcrumb;

0 comments on commit 17ea21a

Please sign in to comment.