[PUI] Quick commands pallet

Fixes #5888
This commit is contained in:
Matthias Mair 2024-04-09 21:03:14 +02:00
parent 374ee51d85
commit 58f4ef21cb
No known key found for this signature in database
GPG Key ID: A593429DDA23B66A
8 changed files with 105 additions and 27 deletions

View File

@ -0,0 +1,15 @@
import { t } from '@lingui/macro';
import { ActionIcon } from '@mantine/core';
import { spotlight } from '@mantine/spotlight';
import { IconCommand } from '@tabler/icons-react';
/**
* A button which opens the quick command modal
*/
export function SpotlightButton() {
return (
<ActionIcon onClick={() => spotlight.open()} title={t`Open spotlight`}>
<IconCommand />
</ActionIcon>
);
}

View File

@ -16,6 +16,10 @@ export interface MenuLinkItem {
docchildren?: React.ReactNode; docchildren?: React.ReactNode;
} }
export type menuItemsCollection = {
[key: string]: MenuLinkItem;
};
function ConditionalDocTooltip({ function ConditionalDocTooltip({
item, item,
children children

View File

@ -10,6 +10,7 @@ import { navTabs as mainNavTabs } from '../../defaults/links';
import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { InvenTreeStyle } from '../../globalStyle'; import { InvenTreeStyle } from '../../globalStyle';
import { apiUrl } from '../../states/ApiState'; import { apiUrl } from '../../states/ApiState';
import { SpotlightButton } from '../buttons/SpotlightButton';
import { ScanButton } from '../items/ScanButton'; import { ScanButton } from '../items/ScanButton';
import { MainMenu } from './MainMenu'; import { MainMenu } from './MainMenu';
import { NavHoverMenu } from './NavHoverMenu'; import { NavHoverMenu } from './NavHoverMenu';
@ -80,6 +81,7 @@ export function Header() {
<ActionIcon onClick={openSearchDrawer}> <ActionIcon onClick={openSearchDrawer}>
<IconSearch /> <IconSearch />
</ActionIcon> </ActionIcon>
<SpotlightButton />
<ScanButton /> <ScanButton />
<ActionIcon onClick={openNotificationDrawer}> <ActionIcon onClick={openNotificationDrawer}>
<Indicator <Indicator

View File

@ -1,6 +1,10 @@
import { t } from '@lingui/macro';
import { Container, Flex, Space } from '@mantine/core'; import { Container, Flex, Space } from '@mantine/core';
import { Navigate, Outlet, useLocation } from 'react-router-dom'; import { SpotlightProvider } from '@mantine/spotlight';
import { IconSearch } from '@tabler/icons-react';
import { Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
import { getActions } from '../../defaults/actions';
import { InvenTreeStyle } from '../../globalStyle'; import { InvenTreeStyle } from '../../globalStyle';
import { useSessionState } from '../../states/SessionState'; import { useSessionState } from '../../states/SessionState';
import { Footer } from './Footer'; import { Footer } from './Footer';
@ -22,17 +26,27 @@ export const ProtectedRoute = ({ children }: { children: JSX.Element }) => {
export default function LayoutComponent() { export default function LayoutComponent() {
const { classes } = InvenTreeStyle(); const { classes } = InvenTreeStyle();
const navigate = useNavigate();
const actions = getActions(navigate);
return ( return (
<ProtectedRoute> <ProtectedRoute>
<Flex direction="column" mih="100vh"> <SpotlightProvider
<Header /> actions={actions}
<Container className={classes.layoutContent} size="100%"> searchIcon={<IconSearch size="1.2rem" />}
<Outlet /> searchPlaceholder={t`Search...`}
</Container> shortcut={['mod + K', '/']}
<Space h="xl" /> nothingFoundMessage={t`Nothing found...`}
<Footer /> >
</Flex> <Flex direction="column" mih="100vh">
<Header />
<Container className={classes.layoutContent} size="100%">
<Outlet />
</Container>
<Space h="xl" />
<Footer />
</Flex>
</SpotlightProvider>
</ProtectedRoute> </ProtectedRoute>
); );
} }

View File

@ -20,6 +20,8 @@ import { useLocalState } from '../../states/LocalState';
import { InvenTreeLogo } from '../items/InvenTreeLogo'; import { InvenTreeLogo } from '../items/InvenTreeLogo';
import { MenuLinks } from '../items/MenuLinks'; import { MenuLinks } from '../items/MenuLinks';
const onlyItems = Object.values(menuItems);
export function NavHoverMenu({ export function NavHoverMenu({
openDrawer: openDrawer openDrawer: openDrawer
}: { }: {
@ -85,7 +87,7 @@ export function NavHoverMenu({
mx="-md" mx="-md"
color={theme.colorScheme === 'dark' ? 'dark.5' : 'gray.1'} color={theme.colorScheme === 'dark' ? 'dark.5' : 'gray.1'}
/> />
<MenuLinks links={menuItems} highlighted={true} /> <MenuLinks links={onlyItems} highlighted={true} />
<div className={classes.headerDropdownFooter}> <div className={classes.headerDropdownFooter}>
<Group position="apart"> <Group position="apart">
<div> <div>

View File

@ -18,6 +18,7 @@ import { MenuLinkItem, MenuLinks } from '../items/MenuLinks';
// TODO @matmair #1: implement plugin loading and menu item generation see #5269 // TODO @matmair #1: implement plugin loading and menu item generation see #5269
const plugins: MenuLinkItem[] = []; const plugins: MenuLinkItem[] = [];
const onlyItems = Object.values(menuItems);
export function NavigationDrawer({ export function NavigationDrawer({
opened, opened,
@ -60,7 +61,7 @@ function DrawerContent() {
<Container className={classes.layoutContent} p={0}> <Container className={classes.layoutContent} p={0}>
<ScrollArea h={scrollHeight} type="always" offsetScrollbars> <ScrollArea h={scrollHeight} type="always" offsetScrollbars>
<Title order={5}>{t`Pages`}</Title> <Title order={5}>{t`Pages`}</Title>
<MenuLinks links={menuItems} /> <MenuLinks links={onlyItems} />
<Space h="md" /> <Space h="md" />
{plugins.length > 0 ? ( {plugins.length > 0 ? (
<> <>

View File

@ -0,0 +1,40 @@
import { t } from '@lingui/macro';
import type { SpotlightAction } from '@mantine/spotlight';
import { IconFileText, IconHome, IconInfoCircle } from '@tabler/icons-react';
import { NavigateFunction } from 'react-router-dom';
import { menuItems } from './menuItems';
export function getActions(navigate: NavigateFunction) {
/**
* Default actions for the spotlight
*/
const actions: SpotlightAction[] = [
{
title: t`Home`,
description: `Go to the home page`,
onTrigger: (action) => navigate(menuItems.home.link),
icon: <IconHome size="1.2rem" />
},
{
title: t`Dashboard`,
description: t`Go to the InvenTree dashboard`,
onTrigger: () => navigate(menuItems.dashboard.link),
icon: <IconHome size="1.2rem" />
},
{
title: t`Documentation`,
description: t`Visit documentation to learn more about InvenTree`,
onTrigger: () => console.log('Documentation'),
icon: <IconFileText size="1.2rem" />
},
{
title: t`About InvenTree`,
description: t`About the InvenTree org`,
onTrigger: () => navigate(menuItems.about.link),
icon: <IconInfoCircle size="1.2rem" />
}
];
return actions;
}

View File

@ -1,75 +1,75 @@
import { Trans } from '@lingui/macro'; import { Trans } from '@lingui/macro';
import { MenuLinkItem } from '../components/items/MenuLinks'; import { menuItemsCollection } from '../components/items/MenuLinks';
import { IS_DEV_OR_DEMO } from '../main'; import { IS_DEV_OR_DEMO } from '../main';
export const menuItems: MenuLinkItem[] = [ export const menuItems: menuItemsCollection = {
{ home: {
id: 'home', id: 'home',
text: <Trans>Home</Trans>, text: <Trans>Home</Trans>,
link: '/', link: '/',
highlight: true highlight: true
}, },
{ profile: {
id: 'profile', id: 'profile',
text: <Trans>Account settings</Trans>, text: <Trans>Account settings</Trans>,
link: '/settings/user', link: '/settings/user',
doctext: <Trans>User attributes and design settings.</Trans> doctext: <Trans>User attributes and design settings.</Trans>
}, },
{ scan: {
id: 'scan', id: 'scan',
text: <Trans>Scanning</Trans>, text: <Trans>Scanning</Trans>,
link: '/scan', link: '/scan',
doctext: <Trans>View for interactive scanning and multiple actions.</Trans>, doctext: <Trans>View for interactive scanning and multiple actions.</Trans>,
highlight: true highlight: true
}, },
{ dashboard: {
id: 'dashboard', id: 'dashboard',
text: <Trans>Dashboard</Trans>, text: <Trans>Dashboard</Trans>,
link: '/dashboard' link: '/dashboard'
}, },
{ parts: {
id: 'parts', id: 'parts',
text: <Trans>Parts</Trans>, text: <Trans>Parts</Trans>,
link: '/part/' link: '/part/'
}, },
{ stock: {
id: 'stock', id: 'stock',
text: <Trans>Stock</Trans>, text: <Trans>Stock</Trans>,
link: '/stock' link: '/stock'
}, },
{ build: {
id: 'build', id: 'build',
text: <Trans>Build</Trans>, text: <Trans>Build</Trans>,
link: '/build/' link: '/build/'
}, },
{ purchasing: {
id: 'purchasing', id: 'purchasing',
text: <Trans>Purchasing</Trans>, text: <Trans>Purchasing</Trans>,
link: '/purchasing/' link: '/purchasing/'
}, },
{ sales: {
id: 'sales', id: 'sales',
text: <Trans>Sales</Trans>, text: <Trans>Sales</Trans>,
link: '/sales/' link: '/sales/'
}, },
{ 'settings-system': {
id: 'settings-system', id: 'settings-system',
text: <Trans>System Settings</Trans>, text: <Trans>System Settings</Trans>,
link: '/settings/system' link: '/settings/system'
}, },
{ 'settings-admin': {
id: 'settings-admin', id: 'settings-admin',
text: <Trans>Admin Center</Trans>, text: <Trans>Admin Center</Trans>,
link: '/settings/admin' link: '/settings/admin'
} }
]; };
if (IS_DEV_OR_DEMO) { if (IS_DEV_OR_DEMO) {
menuItems.push({ menuItems['playground'] = {
id: 'playground', id: 'playground',
text: <Trans>Playground</Trans>, text: <Trans>Playground</Trans>,
link: '/playground', link: '/playground',
highlight: true highlight: true
}); };
} }