mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 19:20:55 +00:00
feat(PUI): Make header tabs links to simpilfy new tab behaviour (#8779)
* add anchor element to tabs to enable opening in new tab * simplify * use unstyled button instead * also enable linking on nav panels * make sure metakey also works (reduces duplication) * remove headers changes * move check for modified key to lib * render nav items as link --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
@ -72,7 +72,7 @@ export const navigateToLink = (
|
|||||||
|
|
||||||
const base = `/${getBaseUrl()}`;
|
const base = `/${getBaseUrl()}`;
|
||||||
|
|
||||||
if (event?.ctrlKey || event?.shiftKey) {
|
if (eventModified(event)) {
|
||||||
// Open the link in a new tab
|
// Open the link in a new tab
|
||||||
let url = link;
|
let url = link;
|
||||||
if (link.startsWith('/') && !link.startsWith(base)) {
|
if (link.startsWith('/') && !link.startsWith(base)) {
|
||||||
@ -91,3 +91,14 @@ export const navigateToLink = (
|
|||||||
navigate(url);
|
navigate(url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the event is modified (e.g. ctrl, shift, or meta key pressed)
|
||||||
|
* @param event - The event to check
|
||||||
|
* @returns true if the event was modified
|
||||||
|
*/
|
||||||
|
export const eventModified = (
|
||||||
|
event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>
|
||||||
|
) => {
|
||||||
|
return event?.ctrlKey || event?.shiftKey || event?.metaKey;
|
||||||
|
};
|
||||||
|
@ -4,6 +4,7 @@ import { useCallback, useMemo } from 'react';
|
|||||||
|
|
||||||
import { ModelInformationDict } from '@lib/enums/ModelInformation';
|
import { ModelInformationDict } from '@lib/enums/ModelInformation';
|
||||||
import type { ModelType } from '@lib/enums/ModelType';
|
import type { ModelType } from '@lib/enums/ModelType';
|
||||||
|
import { eventModified } from '@lib/functions/Navigation';
|
||||||
import { generateUrl } from '../../functions/urls';
|
import { generateUrl } from '../../functions/urls';
|
||||||
import { useServerApiState } from '../../states/ApiState';
|
import { useServerApiState } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
@ -66,7 +67,7 @@ export default function AdminButton(props: Readonly<AdminButtonProps>) {
|
|||||||
`${server.server.django_admin}${modelDef.admin_url}${props.id}/`
|
`${server.server.django_admin}${modelDef.admin_url}${props.id}/`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (event?.ctrlKey || event?.shiftKey) {
|
if (eventModified(event)) {
|
||||||
// Open the link in a new tab
|
// Open the link in a new tab
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,7 +5,8 @@ import {
|
|||||||
Indicator,
|
Indicator,
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
Tooltip
|
Tooltip,
|
||||||
|
UnstyledButton
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useDisclosure, useDocumentVisibility } from '@mantine/hooks';
|
import { useDisclosure, useDocumentVisibility } from '@mantine/hooks';
|
||||||
import { IconBell, IconSearch } from '@tabler/icons-react';
|
import { IconBell, IconSearch } from '@tabler/icons-react';
|
||||||
@ -15,12 +16,14 @@ import { useMatch, useNavigate } from 'react-router-dom';
|
|||||||
|
|
||||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||||
import { apiUrl } from '@lib/functions/Api';
|
import { apiUrl } from '@lib/functions/Api';
|
||||||
|
import { getBaseUrl } from '@lib/functions/Navigation';
|
||||||
import { navigateToLink } from '@lib/functions/Navigation';
|
import { navigateToLink } from '@lib/functions/Navigation';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import type { NavigationUIFeature } from '../../components/plugins/PluginUIFeatureTypes';
|
import type { NavigationUIFeature } from '../../components/plugins/PluginUIFeatureTypes';
|
||||||
import { getNavTabs } from '../../defaults/links';
|
import { getNavTabs } from '../../defaults/links';
|
||||||
|
import { generateUrl } from '../../functions/urls';
|
||||||
import { usePluginUIFeature } from '../../hooks/UsePluginUIFeature';
|
import { usePluginUIFeature } from '../../hooks/UsePluginUIFeature';
|
||||||
import * as classes from '../../main.css';
|
import * as classes from '../../main.css';
|
||||||
import { useServerApiState } from '../../states/ApiState';
|
import { useServerApiState } from '../../states/ApiState';
|
||||||
@ -215,7 +218,12 @@ function NavTabs() {
|
|||||||
navigateToLink(`/${tab.name}`, navigate, event)
|
navigateToLink(`/${tab.name}`, navigate, event)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{tab.title}
|
<UnstyledButton
|
||||||
|
component={'a'}
|
||||||
|
href={generateUrl(`/${getBaseUrl()}/${tab.name}`)}
|
||||||
|
>
|
||||||
|
{tab.title}
|
||||||
|
</UnstyledButton>
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -24,8 +24,11 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import type { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
import type { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||||
import type { ModelType } from '@lib/enums/ModelType';
|
import type { ModelType } from '@lib/enums/ModelType';
|
||||||
import { apiUrl } from '@lib/functions/Api';
|
import { apiUrl } from '@lib/functions/Api';
|
||||||
import { getDetailUrl } from '@lib/functions/Navigation';
|
import {
|
||||||
import { navigateToLink } from '@lib/functions/Navigation';
|
eventModified,
|
||||||
|
getDetailUrl,
|
||||||
|
navigateToLink
|
||||||
|
} from '@lib/functions/Navigation';
|
||||||
import { useApi } from '../../contexts/ApiContext';
|
import { useApi } from '../../contexts/ApiContext';
|
||||||
import { ApiIcon } from '../items/ApiIcon';
|
import { ApiIcon } from '../items/ApiIcon';
|
||||||
import { StylishText } from '../items/StylishText';
|
import { StylishText } from '../items/StylishText';
|
||||||
@ -73,7 +76,7 @@ export default function NavigationTree({
|
|||||||
const follow = useCallback(
|
const follow = useCallback(
|
||||||
(node: TreeNodeData, event?: any) => {
|
(node: TreeNodeData, event?: any) => {
|
||||||
const url = getDetailUrl(modelType, node.value);
|
const url = getDetailUrl(modelType, node.value);
|
||||||
if (event?.shiftKey || event?.ctrlKey) {
|
if (eventModified(event)) {
|
||||||
navigateToLink(url, navigate, event);
|
navigateToLink(url, navigate, event);
|
||||||
} else {
|
} else {
|
||||||
onClose();
|
onClose();
|
||||||
|
@ -38,7 +38,7 @@ import { ModelType } from '@lib/enums/ModelType';
|
|||||||
import { UserRoles } from '@lib/enums/Roles';
|
import { UserRoles } from '@lib/enums/Roles';
|
||||||
import { apiUrl } from '@lib/functions/Api';
|
import { apiUrl } from '@lib/functions/Api';
|
||||||
import { cancelEvent } from '@lib/functions/Events';
|
import { cancelEvent } from '@lib/functions/Events';
|
||||||
import { navigateToLink } from '@lib/functions/Navigation';
|
import { eventModified, navigateToLink } from '@lib/functions/Navigation';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { useUserSettingsState } from '../../states/SettingsState';
|
import { useUserSettingsState } from '../../states/SettingsState';
|
||||||
@ -96,7 +96,7 @@ function QueryResultGroup({
|
|||||||
const url = `${overviewUrl}?search=${searchText}`;
|
const url = `${overviewUrl}?search=${searchText}`;
|
||||||
|
|
||||||
// Close drawer if opening in the same tab
|
// Close drawer if opening in the same tab
|
||||||
if (!(event?.ctrlKey || event?.shiftKey)) {
|
if (!eventModified(event)) {
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ export function SearchDrawer({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event?.ctrlKey || event?.shiftKey) {
|
if (eventModified(event)) {
|
||||||
// Keep the drawer open in this condition
|
// Keep the drawer open in this condition
|
||||||
} else {
|
} else {
|
||||||
closeDrawer();
|
closeDrawer();
|
||||||
|
@ -8,7 +8,8 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
Tooltip
|
Tooltip,
|
||||||
|
UnstyledButton
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconLayoutSidebarLeftCollapse,
|
IconLayoutSidebarLeftCollapse,
|
||||||
@ -32,10 +33,12 @@ import {
|
|||||||
|
|
||||||
import type { ModelType } from '@lib/enums/ModelType';
|
import type { ModelType } from '@lib/enums/ModelType';
|
||||||
import { cancelEvent } from '@lib/functions/Events';
|
import { cancelEvent } from '@lib/functions/Events';
|
||||||
|
import { eventModified, getBaseUrl } from '@lib/functions/Navigation';
|
||||||
import { navigateToLink } from '@lib/functions/Navigation';
|
import { navigateToLink } from '@lib/functions/Navigation';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { identifierString } from '../../functions/conversion';
|
import { identifierString } from '../../functions/conversion';
|
||||||
|
import { generateUrl } from '../../functions/urls';
|
||||||
import { usePluginPanels } from '../../hooks/UsePluginPanels';
|
import { usePluginPanels } from '../../hooks/UsePluginPanels';
|
||||||
import { useLocalState } from '../../states/LocalState';
|
import { useLocalState } from '../../states/LocalState';
|
||||||
import { vars } from '../../theme';
|
import { vars } from '../../theme';
|
||||||
@ -170,9 +173,9 @@ function BasePanelGroup({
|
|||||||
// Callback when the active panel changes
|
// Callback when the active panel changes
|
||||||
const handlePanelChange = useCallback(
|
const handlePanelChange = useCallback(
|
||||||
(targetPanel: string, event?: any) => {
|
(targetPanel: string, event?: any) => {
|
||||||
if (event && (event?.ctrlKey || event?.shiftKey)) {
|
cancelEvent(event);
|
||||||
|
if (event && eventModified(event)) {
|
||||||
const url = `${location.pathname}/../${targetPanel}`;
|
const url = `${location.pathname}/../${targetPanel}`;
|
||||||
cancelEvent(event);
|
|
||||||
navigateToLink(url, navigate, event);
|
navigateToLink(url, navigate, event);
|
||||||
} else {
|
} else {
|
||||||
navigate(`../${targetPanel}`);
|
navigate(`../${targetPanel}`);
|
||||||
@ -252,7 +255,14 @@ function BasePanelGroup({
|
|||||||
handlePanelChange(panel.name, event)
|
handlePanelChange(panel.name, event)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{expanded && panel.label}
|
<UnstyledButton
|
||||||
|
component={'a'}
|
||||||
|
href={generateUrl(
|
||||||
|
`/${getBaseUrl()}${location.pathname}/${panel.name}`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{expanded && panel.label}
|
||||||
|
</UnstyledButton>
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user