2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-27 19:16:44 +00:00

[UI] Row hover (#9465) (#9466)

* [UI] Row hover (#9465)

* Add red color

* Improve table cursor

- Show "pointer" (hand) icon when actions available
- Improve context menu

* Fix import
This commit is contained in:
Oliver 2025-04-06 15:21:17 +10:00 committed by GitHub
parent cf60d809da
commit 8d206bd311
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 60 additions and 13 deletions

View File

@ -3,6 +3,7 @@ import {
Anchor,
Badge,
Group,
type MantineColor,
Paper,
Skeleton,
Stack,
@ -298,9 +299,12 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
value = data?.name;
}
let color: MantineColor | undefined = undefined;
if (value === undefined) {
value = data?.name ?? props.field_data?.backup_value ?? t`No name defined`;
make_link = false;
color = 'red';
}
return (
@ -310,7 +314,7 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
<Text>{value}</Text>
</Anchor>
) : (
<Text>{value}</Text>
<Text c={color}>{value}</Text>
)}
</>
);

View File

@ -11,6 +11,7 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import './styles/overrides.css';
import { api } from './App';
import type { HostList } from './states/states';

View File

@ -0,0 +1,5 @@
/* mantine-datatable overrides */
.mantine-datatable-pointer-cursor,
.mantine-datatable-context-menu-cursor {
cursor: pointer;
}

View File

@ -1,7 +1,10 @@
import { t } from '@lingui/macro';
import { Box, Stack } from '@mantine/core';
import { Box, type MantineStyleProp, Stack } from '@mantine/core';
import { useQuery } from '@tanstack/react-query';
import { useContextMenu } from 'mantine-contextmenu';
import {
type ContextMenuItemOptions,
useContextMenu
} from 'mantine-contextmenu';
import {
DataTable,
type DataTableCellClickHandler,
@ -12,6 +15,7 @@ import type React from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { IconArrowRight } from '@tabler/icons-react';
import { api } from '../App';
import { Boundary } from '../components/Boundary';
import type { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
@ -86,7 +90,7 @@ export type InvenTreeTableProps<T = any> = {
onRowClick?: (record: T, index: number, event: any) => void;
onCellClick?: DataTableCellClickHandler<T>;
modelType?: ModelType;
rowStyle?: (record: T, index: number) => any;
rowStyle?: (record: T, index: number) => MantineStyleProp | undefined;
modelField?: string;
onCellContextMenu?: (record: T, event: any) => void;
minHeight?: number;
@ -568,6 +572,10 @@ export function InvenTreeTable<T extends Record<string, any>>({
[props.onRowClick, props.onCellClick]
);
const supportsContextMenu = useMemo(() => {
return !!props.onCellContextMenu || !!props.rowActions || !!props.modelType;
}, [props.onCellContextMenu, props.rowActions, props.modelType]);
// Callback when a cell is right-clicked
const handleCellContextMenu = ({
record,
@ -583,9 +591,13 @@ export function InvenTreeTable<T extends Record<string, any>>({
}
if (props.onCellContextMenu) {
return props.onCellContextMenu(record, event);
} else if (props.rowActions) {
const empty = () => {};
const items = props.rowActions(record).map((action) => ({
}
const empty = () => {};
let items: ContextMenuItemOptions[] = [];
if (props.rowActions) {
items = props.rowActions(record).map((action) => ({
key: action.title ?? '',
title: action.title ?? '',
color: action.color,
@ -594,10 +606,25 @@ export function InvenTreeTable<T extends Record<string, any>>({
hidden: action.hidden,
disabled: action.disabled
}));
return showContextMenu(items)(event);
} else {
return showContextMenu([])(event);
}
if (props.modelType) {
// Add action to navigate to the detail view
const accessor = props.modelField ?? 'pk';
const pk = resolveItem(record, accessor);
const url = getDetailUrl(props.modelType, pk);
items.push({
key: 'detail',
title: t`View details`,
icon: <IconArrowRight />,
onClick: (event: any) => {
cancelEvent(event);
navigateToLink(url, navigate, event);
}
});
}
return showContextMenu(items)(event);
};
// pagination refresth table if pageSize changes
@ -643,6 +670,14 @@ export function InvenTreeTable<T extends Record<string, any>>({
return optionalParamsa;
}, [tableProps.enablePagination]);
const supportsCellClick = useMemo(() => {
return !!(
tableProps.onCellClick ||
tableProps.onRowClick ||
tableProps.modelType
);
}, [tableProps.onCellClick, tableProps.onRowClick, tableProps.modelType]);
return (
<>
<Stack gap='xs'>
@ -683,12 +718,12 @@ export function InvenTreeTable<T extends Record<string, any>>({
enableSelection ? onSelectedRecordsChange : undefined
}
rowExpansion={rowExpansion}
rowStyle={tableProps.rowStyle}
// rowStyle={rowStyleCallback}
fetching={isFetching}
noRecordsText={missingRecordsText}
records={tableState.records}
columns={dataColumns}
onCellClick={handleCellClick}
onCellClick={supportsCellClick ? handleCellClick : undefined}
noHeader={tableProps.noHeader ?? false}
defaultColumnProps={{
noWrap: true,
@ -698,7 +733,9 @@ export function InvenTreeTable<T extends Record<string, any>>({
overflow: 'hidden'
})
}}
onCellContextMenu={handleCellContextMenu}
onCellContextMenu={
supportsContextMenu ? handleCellContextMenu : undefined
}
{...optionalParams}
/>
</Box>