2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-05-30 21:25:36 +00:00

[UI] Hover instance (#12038)

* Add new user setting

* Implement hover-card for RenderInstance

* Allow override when calling RenderInstance

* Simplify

* Add link icon

* Adjust logic

* Simplify logic

* update playwright

Ref: https://github.com/microsoft/playwright/issues/40998
This commit is contained in:
Oliver
2026-05-30 23:00:52 +10:00
committed by GitHub
parent d2bec03d93
commit 5832718637
6 changed files with 107 additions and 6 deletions
+1
View File
@@ -29,6 +29,7 @@ The *Display Settings* screen shows general display configuration options:
{{ usersetting("SHOW_FULL_CATEGORY_IN_TABLES")}}
{{ usersetting("SHOW_BOM_SUBASSEMBLY_LEVELS")}}
{{ usersetting("ENABLE_LAST_BREADCRUMB") }}
{{ usersetting("SHOW_EXTRA_MODEL_INFO") }}
{{ usersetting("SHOW_FULL_LOCATION_IN_TABLES") }}
{{ usersetting("DISPLAY_ITEMS_FINAL_LEVEL") }}
@@ -235,6 +235,12 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
'default': False,
'validator': bool,
},
'SHOW_EXTRA_MODEL_INFO': {
'name': _('Show Extra Model Information'),
'description': _('Display extra information in model selection dropdowns'),
'default': False,
'validator': bool,
},
'SHOW_FULL_LOCATION_IN_TABLES': {
'name': _('Show full stock location in tables'),
'description': _(
@@ -10,6 +10,7 @@ export interface ModelInformationInterface {
url_detail?: string;
api_endpoint: ApiEndpoints;
admin_url?: string;
pk_field?: string;
supports_barcode?: boolean;
icon: keyof InvenTreeIconType;
}
+1
View File
@@ -15,6 +15,7 @@ export interface InstanceRenderInterface {
link?: boolean;
navigate?: any;
showSecondary?: boolean;
showHover?: boolean;
extra?: Record<string, any>;
}
@@ -1,16 +1,20 @@
import { t } from '@lingui/core/macro';
import {
ActionIcon,
Alert,
Anchor,
Box,
Group,
HoverCard,
type MantineSize,
Paper,
Skeleton,
Space,
Stack,
Text
} from '@mantine/core';
import { useQuery } from '@tanstack/react-query';
import { type ReactNode, useCallback } from 'react';
import { type ReactNode, useCallback, useMemo } from 'react';
import { ModelInformationDict } from '@lib/enums/ModelInformation';
import { ModelType } from '@lib/enums/ModelType';
@@ -25,8 +29,11 @@ import type {
export type { InstanceRenderInterface } from '@lib/types/Rendering';
import { getBaseUrl, navigateToLink, shortenString } from '@lib/index';
import { IconLink } from '@tabler/icons-react';
import { useNavigate } from 'react-router-dom';
import { useApi } from '../../contexts/ApiContext';
import { usePluginState } from '../../states/PluginState';
import { useUserSettingsState } from '../../states/SettingsStates';
import { Thumbnail } from '../images/Thumbnail';
import { RenderBuildItem, RenderBuildLine, RenderBuildOrder } from './Build';
import {
@@ -125,11 +132,95 @@ export function RenderInstance(props: RenderInstanceProps): ReactNode {
props.custom_model ?? props.model ?? ''
);
// provider component
if (!RenderComponent) {
return <UnknownRenderer model={props.model} />;
const navigate = useNavigate();
const userSettings = useUserSettingsState();
// Extract model information from the defined model type
const modelInfo = useMemo(() => {
if (!props.model) {
return undefined;
}
return <RenderComponent {...props} />;
return ModelInformationDict[
props.model.toString().toLowerCase() as ModelType
];
}, [props.model]);
const showHover: boolean = useMemo(() => {
if (!modelInfo) {
return false;
}
// Override with the props.showHover attribute
if (props.showHover !== undefined) {
return props.showHover;
}
// If not specified, fall back to the user configured setting
return userSettings.isSet('SHOW_EXTRA_MODEL_INFO');
}, [props.showHover, modelInfo, userSettings]);
// Extract model ID from the provided instance data, using the defined primary key field (or 'pk' as a fallback)
const modelId = useMemo(() => {
if (!modelInfo || !props.instance) {
return undefined;
}
return props.instance[modelInfo.pk_field ?? 'pk'];
}, [modelInfo, props.instance]);
const detailUrl = useMemo(() => {
if (!modelInfo || !modelId || !modelInfo.url_detail) {
return undefined;
}
return modelInfo.url_detail.replace(':pk', modelId.toString());
}, [modelInfo, modelId]);
return (
<HoverCard
disabled={!showHover}
position='top-end'
withinPortal
openDelay={500}
closeDelay={100}
zIndex={99999}
>
<HoverCard.Target>
<Box>
{!!RenderComponent ? (
<RenderComponent {...props} />
) : (
<UnknownRenderer model={props.model} />
)}
</Box>
</HoverCard.Target>
<HoverCard.Dropdown>
<Stack gap='xs'>
<Group justify='space-between'>
<Text size='sm' fw='bold'>
{modelInfo?.label()}
</Text>
{modelId && <Text size='xs'>{`[${t`ID`}: ${modelId}]`}</Text>}
</Group>
{detailUrl && (
<Anchor
href={detailUrl}
target='_blank'
onClick={(event) => navigateToLink(detailUrl, navigate, event)}
>
<Group gap='xs' wrap='nowrap'>
<ActionIcon variant='transparent' size='xs'>
<IconLink />
</ActionIcon>
<Text size='sm'>{t`View details`}</Text>
</Group>
</Anchor>
)}
</Stack>
</HoverCard.Dropdown>
</HoverCard>
);
}
export function RenderRemoteInstance({
@@ -61,6 +61,7 @@ export default function UserSettings() {
'FORMS_CLOSE_USING_ESCAPE',
'DISPLAY_STOCKTAKE_TAB',
'ENABLE_LAST_BREADCRUMB',
'SHOW_EXTRA_MODEL_INFO',
'SHOW_FULL_LOCATION_IN_TABLES',
'SHOW_FULL_CATEGORY_IN_TABLES',
'SHOW_BOM_SUBASSEMBLY_LEVELS'