diff --git a/src/frontend/src/components/nav/BreadcrumbList.tsx b/src/frontend/src/components/nav/BreadcrumbList.tsx
index dbd5ca0f30..15c7374975 100644
--- a/src/frontend/src/components/nav/BreadcrumbList.tsx
+++ b/src/frontend/src/components/nav/BreadcrumbList.tsx
@@ -10,6 +10,8 @@ import { IconMenu2 } from '@tabler/icons-react';
import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
+import { navigateToLink } from '../../functions/navigation';
+
export type Breadcrumb = {
name: string;
url: string;
@@ -57,7 +59,10 @@ export function BreadcrumbList({
return (
breadcrumb.url && navigate(breadcrumb.url)}
+ onClick={(event: any) =>
+ breadcrumb.url &&
+ navigateToLink(breadcrumb.url, navigate, event)
+ }
>
{breadcrumb.name}
diff --git a/src/frontend/src/components/nav/Header.tsx b/src/frontend/src/components/nav/Header.tsx
index f5cdd07865..af8c8b1126 100644
--- a/src/frontend/src/components/nav/Header.tsx
+++ b/src/frontend/src/components/nav/Header.tsx
@@ -8,6 +8,7 @@ import { useMatch, useNavigate } from 'react-router-dom';
import { api } from '../../App';
import { navTabs as mainNavTabs } from '../../defaults/links';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
+import { navigateToLink } from '../../functions/navigation';
import * as classes from '../../main.css';
import { apiUrl } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
@@ -141,13 +142,16 @@ function NavTabs() {
tab: classes.tab
}}
value={tabValue}
- onChange={(value) =>
- value == '/' ? navigate('/') : navigate(`/${value}`)
- }
>
{mainNavTabs.map((tab) => (
-
+
+ navigateToLink(`/${tab.name}`, navigate, event)
+ }
+ >
{tab.text}
))}
diff --git a/src/frontend/src/components/nav/PanelGroup.tsx b/src/frontend/src/components/nav/PanelGroup.tsx
index afc9be16d6..5523de022c 100644
--- a/src/frontend/src/components/nav/PanelGroup.tsx
+++ b/src/frontend/src/components/nav/PanelGroup.tsx
@@ -10,15 +10,17 @@ import {
IconLayoutSidebarLeftCollapse,
IconLayoutSidebarRightCollapse
} from '@tabler/icons-react';
-import { ReactNode, useEffect, useMemo, useState } from 'react';
+import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
import {
Navigate,
Route,
Routes,
+ useLocation,
useNavigate,
useParams
} from 'react-router-dom';
+import { navigateToLink } from '../../functions/navigation';
import { useLocalState } from '../../states/LocalState';
import { Boundary } from '../Boundary';
import { PlaceholderPanel } from '../items/Placeholder';
@@ -52,6 +54,7 @@ function BasePanelGroup({
selectedPanel,
collapsible = true
}: Readonly): ReactNode {
+ const location = useLocation();
const navigate = useNavigate();
const { panel } = useParams();
@@ -72,19 +75,27 @@ function BasePanelGroup({
}, [setLastUsedPanel]);
// Callback when the active panel changes
- function handlePanelChange(panel: string | null) {
- if (activePanels.findIndex((p) => p.name === panel) === -1) {
- setLastUsedPanel('');
- return navigate('../');
- }
+ const handlePanelChange = useCallback(
+ (panel: string | null, event?: any) => {
+ if (activePanels.findIndex((p) => p.name === panel) === -1) {
+ setLastUsedPanel('');
+ return navigate('../');
+ }
- navigate(`../${panel}`);
+ if (event && (event?.ctrlKey || event?.shiftKey)) {
+ const url = `${location.pathname}/../${panel}`;
+ navigateToLink(url, navigate, event);
+ } else {
+ navigate(`../${panel}`);
+ }
- // Optionally call external callback hook
- if (panel && onPanelChange) {
- onPanelChange(panel);
- }
- }
+ // Optionally call external callback hook
+ if (panel && onPanelChange) {
+ onPanelChange(panel);
+ }
+ },
+ [activePanels, setLastUsedPanel, navigate, location, onPanelChange]
+ );
// if the selected panel state changes update the current panel
useEffect(() => {
@@ -129,6 +140,9 @@ function BasePanelGroup({
hidden={panel.hidden}
disabled={panel.disabled}
style={{ cursor: panel.disabled ? 'unset' : 'pointer' }}
+ onClick={(event: any) =>
+ handlePanelChange(panel.name, event)
+ }
>
{expanded && panel.label}