mirror of
https://github.com/inventree/InvenTree.git
synced 2026-06-12 03:28:37 +00:00
feat(frontend): add hotkey registration function and hotkey helper modal (#12128)
* add new hotkey registration interface and hotkey modal * fix import * add printing hotkey * add todo * add hotkey for barcode scanning * register spotlight shortcut key * sort keys * render nicer overview * fix props * expose for plugins --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
This file contains historical changelog information for the InvenTree UI components library.
|
This file contains historical changelog information for the InvenTree UI components library.
|
||||||
|
|
||||||
|
### 1.4.6 - June 2026
|
||||||
|
|
||||||
|
Adds `useInvenTreeHotkeys` hook that mirrors mantine's `useHotkeys` hook, but adds visibility of the hotkeys to the user interface.
|
||||||
|
|
||||||
### 1.4.5 - June 2026
|
### 1.4.5 - June 2026
|
||||||
|
|
||||||
Fixes callback signature for `<LocalizedComponent>` to allow for an optional `loadLocale` function to be passed in, which is used to dynamically load locale messages for the plugin.
|
Fixes callback signature for `<LocalizedComponent>` to allow for an optional `loadLocale` function to be passed in, which is used to dynamically load locale messages for the plugin.
|
||||||
|
|||||||
@@ -1,6 +1,37 @@
|
|||||||
|
import { useHotkeys } from '@mantine/hooks';
|
||||||
|
import type { HotkeyItemOptions } from '@mantine/hooks';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useLocalLibState } from '..';
|
||||||
|
|
||||||
// Helper function to cancel event propagation
|
// Helper function to cancel event propagation
|
||||||
export function cancelEvent(event: any) {
|
export function cancelEvent(event: any) {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
event?.stopPropagation();
|
event?.stopPropagation();
|
||||||
event?.nativeEvent?.stopImmediatePropagation();
|
event?.nativeEvent?.stopImmediatePropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InvenTreeHotkeyItem = [
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
(event: KeyboardEvent) => void,
|
||||||
|
HotkeyItemOptions?
|
||||||
|
];
|
||||||
|
|
||||||
|
export function useInvenTreeHotkeys(hotkeys: InvenTreeHotkeyItem[]) {
|
||||||
|
// Register the hotkeys using the Mantine hook
|
||||||
|
useHotkeys(
|
||||||
|
hotkeys.map(([key, _, handler, options]) => [key, handler, options])
|
||||||
|
);
|
||||||
|
|
||||||
|
// register to helper state to store hotkeys
|
||||||
|
// This allows us to display the hotkeys in the UI
|
||||||
|
const keyelems: [string, string][] = hotkeys.map(([key, description]) => [
|
||||||
|
key,
|
||||||
|
description
|
||||||
|
]);
|
||||||
|
useEffect(() => {
|
||||||
|
useLocalLibState.getState().addHotkeys(keyelems);
|
||||||
|
return () =>
|
||||||
|
useLocalLibState.getState().removeHotkeys(keyelems.map(([key]) => key));
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ export {
|
|||||||
type TableStateExtraProps
|
type TableStateExtraProps
|
||||||
} from './hooks/UseTable';
|
} from './hooks/UseTable';
|
||||||
|
|
||||||
|
export { useInvenTreeHotkeys } from './functions/Events';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type DrawerProps,
|
type DrawerProps,
|
||||||
DetailDrawer,
|
DetailDrawer,
|
||||||
|
|||||||
@@ -10,8 +10,24 @@ export const useLocalLibState = create<LocalLibStateProps>()(
|
|||||||
detailDrawerStack:
|
detailDrawerStack:
|
||||||
value === false ? 0 : get().detailDrawerStack + value
|
value === false ? 0 : get().detailDrawerStack + value
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
hotkeys: {},
|
||||||
|
addHotkeys: (hotkeys) => {
|
||||||
|
const newHotkeys = { ...get().hotkeys };
|
||||||
|
for (const [ref, details] of hotkeys) {
|
||||||
|
newHotkeys[ref] = details;
|
||||||
|
}
|
||||||
|
set({ hotkeys: newHotkeys });
|
||||||
|
},
|
||||||
|
removeHotkeys: (hotkeys) => {
|
||||||
|
const newHotkeys = { ...get().hotkeys };
|
||||||
|
for (const ref of hotkeys) {
|
||||||
|
delete newHotkeys[ref];
|
||||||
|
}
|
||||||
|
set({ hotkeys: newHotkeys });
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'session-settings-inventreedb_lib'
|
name: 'session-settings-inventreedb_lib'
|
||||||
}
|
}
|
||||||
@@ -20,4 +36,7 @@ export const useLocalLibState = create<LocalLibStateProps>()(
|
|||||||
export interface LocalLibStateProps {
|
export interface LocalLibStateProps {
|
||||||
detailDrawerStack: number;
|
detailDrawerStack: number;
|
||||||
addDetailDrawer: (value: number | false) => void;
|
addDetailDrawer: (value: number | false) => void;
|
||||||
|
hotkeys: Record<string, string>;
|
||||||
|
addHotkeys: (hotkeys: [string, string][]) => void;
|
||||||
|
removeHotkeys: (hotkeys: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@inventreedb/ui",
|
"name": "@inventreedb/ui",
|
||||||
"description": "UI components for the InvenTree project",
|
"description": "UI components for the InvenTree project",
|
||||||
"version": "1.4.5",
|
"version": "1.4.6",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
import { 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 { useInvenTreeHotkeys } from '@lib/functions/Events';
|
||||||
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { IconPrinter, IconReport, IconTags } from '@tabler/icons-react';
|
import { IconPrinter, IconReport, IconTags } from '@tabler/icons-react';
|
||||||
@@ -34,6 +35,33 @@ export function PrintingActions({
|
|||||||
|
|
||||||
const enabled = useMemo(() => items.length > 0, [items]);
|
const enabled = useMemo(() => items.length > 0, [items]);
|
||||||
|
|
||||||
|
useInvenTreeHotkeys([
|
||||||
|
[
|
||||||
|
'mod+P',
|
||||||
|
t`Open Print Report dialog`,
|
||||||
|
(event) => {
|
||||||
|
if (event.repeat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (enabled && !hidden) {
|
||||||
|
reportModal.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'mod+L',
|
||||||
|
t`Open Print Label dialog`,
|
||||||
|
(event) => {
|
||||||
|
if (event.repeat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (enabled && !hidden) {
|
||||||
|
labelModal.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
const defaultLabelPlugin = useMemo(
|
const defaultLabelPlugin = useMemo(
|
||||||
() => userSettings.getSetting('LABEL_DEFAULT_PRINTER'),
|
() => userSettings.getSetting('LABEL_DEFAULT_PRINTER'),
|
||||||
[userSettings]
|
[userSettings]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useInvenTreeHotkeys } from '@lib/functions/Events';
|
||||||
import type { ModelType } from '@lib/index';
|
import type { ModelType } from '@lib/index';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { ActionIcon, Tooltip } from '@mantine/core';
|
import { ActionIcon, Tooltip } from '@mantine/core';
|
||||||
@@ -14,14 +15,28 @@ import BarcodeScanDialog, {
|
|||||||
export function ScanButton({
|
export function ScanButton({
|
||||||
modelType,
|
modelType,
|
||||||
callback,
|
callback,
|
||||||
onScanSuccess
|
onScanSuccess,
|
||||||
|
hotkey = false
|
||||||
}: {
|
}: {
|
||||||
modelType?: ModelType;
|
modelType?: ModelType;
|
||||||
callback?: BarcodeScanCallback;
|
callback?: BarcodeScanCallback;
|
||||||
onScanSuccess?: BarcodeScanSuccessCallback;
|
onScanSuccess?: BarcodeScanSuccessCallback;
|
||||||
|
hotkey?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [opened, { open, close }] = useDisclosure(false);
|
const [opened, { open, close }] = useDisclosure(false);
|
||||||
|
|
||||||
|
if (hotkey) {
|
||||||
|
useInvenTreeHotkeys([
|
||||||
|
[
|
||||||
|
'mod+b',
|
||||||
|
t`Open barcode scanner`,
|
||||||
|
() => {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Tooltip position='bottom-end' label={t`Scan Barcode`}>
|
<Tooltip position='bottom-end' label={t`Scan Barcode`}>
|
||||||
|
|||||||
@@ -2,12 +2,18 @@ import { t } from '@lingui/core/macro';
|
|||||||
import { ActionIcon, Tooltip } from '@mantine/core';
|
import { ActionIcon, Tooltip } from '@mantine/core';
|
||||||
import { IconCommand } from '@tabler/icons-react';
|
import { IconCommand } from '@tabler/icons-react';
|
||||||
|
|
||||||
import { firstSpotlight } from '../nav/Layout';
|
import { useLocalLibState } from '@lib/states/LocalLibState';
|
||||||
|
import { firstSpotlight, searchShortcutKey } from '../nav/Layout';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A button which opens the quick command modal
|
* A button which opens the quick command modal
|
||||||
*/
|
*/
|
||||||
export function SpotlightButton() {
|
export function SpotlightButton({ hotkey = false }: { hotkey?: boolean }) {
|
||||||
|
if (hotkey) {
|
||||||
|
useLocalLibState
|
||||||
|
.getState()
|
||||||
|
.addHotkeys([[searchShortcutKey, t`Open spotlight`]]);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Tooltip position='bottom-end' label={t`Open spotlight`}>
|
<Tooltip position='bottom-end' label={t`Open spotlight`}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Text
|
Text
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useDisclosure, useHotkeys } from '@mantine/hooks';
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
import { IconExclamationCircle, IconInfoCircle } from '@tabler/icons-react';
|
import { IconExclamationCircle, IconInfoCircle } from '@tabler/icons-react';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { type Layout, Responsive, WidthProvider } from 'react-grid-layout';
|
import { type Layout, Responsive, WidthProvider } from 'react-grid-layout';
|
||||||
|
|
||||||
|
import { useInvenTreeHotkeys } from '@lib/functions/Events';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { useDashboardItems } from '../../hooks/UseDashboardItems';
|
import { useDashboardItems } from '../../hooks/UseDashboardItems';
|
||||||
import { useLocalState } from '../../states/LocalState';
|
import { useLocalState } from '../../states/LocalState';
|
||||||
@@ -61,9 +62,10 @@ export default function DashboardLayout() {
|
|||||||
const [loaded, setLoaded] = useState(false);
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
// Keyboard shortcut for editing the dashboard layout
|
// Keyboard shortcut for editing the dashboard layout
|
||||||
useHotkeys([
|
useInvenTreeHotkeys([
|
||||||
[
|
[
|
||||||
'mod+E',
|
'mod+E',
|
||||||
|
t`Toggle dashboard edit mode`,
|
||||||
() => {
|
() => {
|
||||||
setEditing.toggle();
|
setEditing.toggle();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { useLocalLibState } from '@lib/states/LocalLibState';
|
||||||
|
import { Kbd, Table } from '@mantine/core';
|
||||||
|
import { type UseOSReturnValue, useOs } from '@mantine/hooks';
|
||||||
|
import type { ContextModalProps } from '@mantine/modals';
|
||||||
|
import { Fragment, useMemo } from 'react';
|
||||||
|
|
||||||
|
function modRenderer(value: string, os: UseOSReturnValue) {
|
||||||
|
if (os === 'macos') {
|
||||||
|
return value.replace('mod', '⌘');
|
||||||
|
}
|
||||||
|
return value.replace('mod', 'Ctrl');
|
||||||
|
}
|
||||||
|
|
||||||
|
function kbdRenderer(value: string, os: UseOSReturnValue) {
|
||||||
|
const parts = value.split('+');
|
||||||
|
if (parts.length > 1) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{parts.map((part, idx) => (
|
||||||
|
<Fragment key={idx}>
|
||||||
|
<Kbd>{modRenderer(part, os)}</Kbd>
|
||||||
|
{idx < parts.length - 1 && ' + '}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <Kbd>{modRenderer(value, os)}</Kbd>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HotkeyModal({
|
||||||
|
context,
|
||||||
|
id
|
||||||
|
}: ContextModalProps<{ modalBody: string }>) {
|
||||||
|
const os = useOs();
|
||||||
|
|
||||||
|
const hotkeys = useMemo(() => {
|
||||||
|
const keys = Object.entries(useLocalLibState.getState().hotkeys).map(
|
||||||
|
([hotkey, description]) => {
|
||||||
|
return {
|
||||||
|
key: hotkey,
|
||||||
|
dec: description
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
keys.sort((a, b) => a.key.localeCompare(b.key));
|
||||||
|
return keys;
|
||||||
|
}, []);
|
||||||
|
const data = useMemo(() => {
|
||||||
|
return {
|
||||||
|
head: ['Hotkey', 'Action'],
|
||||||
|
body: [...hotkeys.map((item) => [kbdRenderer(item.key, os), item.dec])]
|
||||||
|
};
|
||||||
|
}, [os, hotkeys]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
striped
|
||||||
|
highlightOnHover
|
||||||
|
withColumnBorders
|
||||||
|
horizontalSpacing='md'
|
||||||
|
verticalSpacing='xs'
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -10,11 +10,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
UnstyledButton
|
UnstyledButton
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import { useDisclosure, useDocumentVisibility } from '@mantine/hooks';
|
||||||
useDisclosure,
|
|
||||||
useDocumentVisibility,
|
|
||||||
useHotkeys
|
|
||||||
} from '@mantine/hooks';
|
|
||||||
import { IconBell, IconSearch, IconUserBolt } from '@tabler/icons-react';
|
import { IconBell, IconSearch, IconUserBolt } from '@tabler/icons-react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { type ReactNode, useEffect, useMemo, useState } from 'react';
|
import { type ReactNode, useEffect, useMemo, useState } from 'react';
|
||||||
@@ -22,6 +18,7 @@ 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 { useInvenTreeHotkeys } from '@lib/functions/Events';
|
||||||
import { getBaseUrl } from '@lib/functions/Navigation';
|
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';
|
||||||
@@ -60,15 +57,17 @@ export function Header() {
|
|||||||
{ open: openSearchDrawer, close: closeSearchDrawer }
|
{ open: openSearchDrawer, close: closeSearchDrawer }
|
||||||
] = useDisclosure(false);
|
] = useDisclosure(false);
|
||||||
|
|
||||||
useHotkeys([
|
useInvenTreeHotkeys([
|
||||||
[
|
[
|
||||||
'/',
|
'/',
|
||||||
|
t`Open search`,
|
||||||
() => {
|
() => {
|
||||||
openSearchDrawer();
|
openSearchDrawer();
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'mod+/',
|
'mod+/',
|
||||||
|
t`Open search`,
|
||||||
() => {
|
() => {
|
||||||
openSearchDrawer();
|
openSearchDrawer();
|
||||||
}
|
}
|
||||||
@@ -198,8 +197,8 @@ export function Header() {
|
|||||||
<IconSearch />
|
<IconSearch />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{userSettings.isSet('SHOW_SPOTLIGHT') && <SpotlightButton />}
|
{userSettings.isSet('SHOW_SPOTLIGHT') && <SpotlightButton hotkey />}
|
||||||
{globalSettings.isSet('BARCODE_ENABLE') && <ScanButton />}
|
{globalSettings.isSet('BARCODE_ENABLE') && <ScanButton hotkey />}
|
||||||
<Indicator
|
<Indicator
|
||||||
radius='lg'
|
radius='lg'
|
||||||
size='18'
|
size='18'
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ export const ProtectedRoute = ({ children }: { children: JSX.Element }) => {
|
|||||||
|
|
||||||
export const [firstStore, firstSpotlight] = createSpotlight();
|
export const [firstStore, firstSpotlight] = createSpotlight();
|
||||||
|
|
||||||
|
export const searchShortcutKey = 'mod+K';
|
||||||
|
|
||||||
export default function LayoutComponent() {
|
export default function LayoutComponent() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -140,7 +142,7 @@ export default function LayoutComponent() {
|
|||||||
leftSection: <IconSearch size='1.2rem' />,
|
leftSection: <IconSearch size='1.2rem' />,
|
||||||
placeholder: t`Search...`
|
placeholder: t`Search...`
|
||||||
}}
|
}}
|
||||||
shortcut={['mod + K']}
|
shortcut={[searchShortcutKey]}
|
||||||
nothingFound={t`Nothing found...`}
|
nothingFound={t`Nothing found...`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Group, Paper, Space, Stack, Text } from '@mantine/core';
|
import { Group, Paper, Space, Stack, Text } from '@mantine/core';
|
||||||
import { useHotkeys } from '@mantine/hooks';
|
|
||||||
|
|
||||||
import { StylishText } from '@lib/components/StylishText';
|
import { StylishText } from '@lib/components/StylishText';
|
||||||
|
import { useInvenTreeHotkeys } from '@lib/functions/Events';
|
||||||
import { shortenString } from '@lib/functions/String';
|
import { shortenString } from '@lib/functions/String';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
import { Fragment, type ReactNode, useMemo } from 'react';
|
import { Fragment, type ReactNode, useMemo } from 'react';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import { usePluginUIFeature } from '../../hooks/UsePluginUIFeature';
|
import { usePluginUIFeature } from '../../hooks/UsePluginUIFeature';
|
||||||
@@ -52,9 +53,11 @@ export function PageDetail({
|
|||||||
const userSettings = useUserSettingsState();
|
const userSettings = useUserSettingsState();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
useHotkeys([
|
|
||||||
|
useInvenTreeHotkeys([
|
||||||
[
|
[
|
||||||
'mod+E',
|
'mod+E',
|
||||||
|
title ? t`Edit ${title}` : t`Edit`,
|
||||||
(event) => {
|
(event) => {
|
||||||
if (event.repeat) {
|
if (event.repeat) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { ContextMenuProvider } from 'mantine-contextmenu';
|
|||||||
import type { JSX } from 'react';
|
import type { JSX } from 'react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { AboutInvenTreeModal } from '../components/modals/AboutInvenTreeModal';
|
import { AboutInvenTreeModal } from '../components/modals/AboutInvenTreeModal';
|
||||||
|
import { HotkeyModal } from '../components/modals/HotkeyModal';
|
||||||
import { LicenseModal } from '../components/modals/LicenseModal';
|
import { LicenseModal } from '../components/modals/LicenseModal';
|
||||||
import { QrModal } from '../components/modals/QrModal';
|
import { QrModal } from '../components/modals/QrModal';
|
||||||
import { ServerInfoModal } from '../components/modals/ServerInfoModal';
|
import { ServerInfoModal } from '../components/modals/ServerInfoModal';
|
||||||
@@ -59,7 +60,8 @@ export function ThemeContext({
|
|||||||
info: ServerInfoModal,
|
info: ServerInfoModal,
|
||||||
about: AboutInvenTreeModal,
|
about: AboutInvenTreeModal,
|
||||||
license: LicenseModal,
|
license: LicenseModal,
|
||||||
qr: QrModal
|
qr: QrModal,
|
||||||
|
hotkey: HotkeyModal
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Notifications />
|
<Notifications />
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
import type { NavigateFunction } from 'react-router-dom';
|
import type { NavigateFunction } from 'react-router-dom';
|
||||||
|
|
||||||
import { ModelInformationDict } from '@lib/enums/ModelInformation';
|
import { ModelInformationDict } from '@lib/enums/ModelInformation';
|
||||||
import { ModelType, UserRoles } from '@lib/index';
|
import { ModelType, StylishText, UserRoles } from '@lib/index';
|
||||||
|
import { Trans } from '@lingui/react/macro';
|
||||||
import { openContextModal } from '@mantine/modals';
|
import { openContextModal } from '@mantine/modals';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
@@ -23,13 +24,26 @@ import { useGlobalSettingsState } from '../states/SettingsStates';
|
|||||||
import { useUserState } from '../states/UserState';
|
import { useUserState } from '../states/UserState';
|
||||||
import { aboutInvenTree, docLinks, licenseInfo, serverInfo } from './links';
|
import { aboutInvenTree, docLinks, licenseInfo, serverInfo } from './links';
|
||||||
|
|
||||||
export function openQrModal(navigate: NavigateFunction) {
|
function openQrModal(navigate: NavigateFunction) {
|
||||||
return openContextModal({
|
return openContextModal({
|
||||||
modal: 'qr',
|
modal: 'qr',
|
||||||
innerProps: { navigate: navigate }
|
innerProps: { navigate: navigate }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openHotkeys() {
|
||||||
|
return openContextModal({
|
||||||
|
modal: 'hotkey',
|
||||||
|
title: (
|
||||||
|
<StylishText size='xl'>
|
||||||
|
<Trans>Hotkeys</Trans>
|
||||||
|
</StylishText>
|
||||||
|
),
|
||||||
|
size: 'xl',
|
||||||
|
innerProps: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getActions(navigate: NavigateFunction) {
|
export function getActions(navigate: NavigateFunction) {
|
||||||
const setNavigationOpen = useLocalState(
|
const setNavigationOpen = useLocalState(
|
||||||
useShallow((state) => state.setNavigationOpen)
|
useShallow((state) => state.setNavigationOpen)
|
||||||
@@ -91,6 +105,13 @@ export function getActions(navigate: NavigateFunction) {
|
|||||||
description: t`Go to your user settings`,
|
description: t`Go to your user settings`,
|
||||||
onClick: () => navigate('/settings/user'),
|
onClick: () => navigate('/settings/user'),
|
||||||
leftSection: <IconUserCog size='1.2rem' />
|
leftSection: <IconUserCog size='1.2rem' />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hotkeys',
|
||||||
|
label: t`Hotkeys`,
|
||||||
|
description: t`View a list of available hotkeys`,
|
||||||
|
onClick: () => openHotkeys(),
|
||||||
|
leftSection: <IconSettings size='1.2rem' />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user