mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
[UI] Row hover (#9465)
* Add red color * Improve table cursor - Show "pointer" (hand) icon when actions available - Improve context menu
This commit is contained in:
parent
6b28bcca70
commit
ac181140c9
@ -5,6 +5,7 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
Group,
|
Group,
|
||||||
HoverCard,
|
HoverCard,
|
||||||
|
type MantineColor,
|
||||||
Paper,
|
Paper,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
@ -380,9 +381,12 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
|||||||
value = data?.name;
|
value = data?.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let color: MantineColor | undefined = undefined;
|
||||||
|
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
value = data?.name ?? props.field_data?.backup_value ?? t`No name defined`;
|
value = data?.name ?? props.field_data?.backup_value ?? t`No name defined`;
|
||||||
make_link = false;
|
make_link = false;
|
||||||
|
color = 'red';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -392,7 +396,7 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
|||||||
<Text>{value}</Text>
|
<Text>{value}</Text>
|
||||||
</Anchor>
|
</Anchor>
|
||||||
) : (
|
) : (
|
||||||
<Text>{value}</Text>
|
<Text c={color}>{value}</Text>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -11,6 +11,7 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import 'react-grid-layout/css/styles.css';
|
import 'react-grid-layout/css/styles.css';
|
||||||
import 'react-resizable/css/styles.css';
|
import 'react-resizable/css/styles.css';
|
||||||
|
import './styles/overrides.css';
|
||||||
|
|
||||||
import type { HostList } from './states/states';
|
import type { HostList } from './states/states';
|
||||||
import MainView from './views/MainView';
|
import MainView from './views/MainView';
|
||||||
|
5
src/frontend/src/styles/overrides.css
Normal file
5
src/frontend/src/styles/overrides.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/* mantine-datatable overrides */
|
||||||
|
.mantine-datatable-pointer-cursor,
|
||||||
|
.mantine-datatable-context-menu-cursor {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Box, Stack } from '@mantine/core';
|
import { Box, type MantineStyleProp, Stack } from '@mantine/core';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useContextMenu } from 'mantine-contextmenu';
|
import {
|
||||||
|
type ContextMenuItemOptions,
|
||||||
|
useContextMenu
|
||||||
|
} from 'mantine-contextmenu';
|
||||||
import {
|
import {
|
||||||
DataTable,
|
DataTable,
|
||||||
type DataTableCellClickHandler,
|
type DataTableCellClickHandler,
|
||||||
@ -13,6 +16,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { hideNotification, showNotification } from '@mantine/notifications';
|
import { hideNotification, showNotification } from '@mantine/notifications';
|
||||||
|
import { IconArrowRight } from '@tabler/icons-react';
|
||||||
import { Boundary } from '../components/Boundary';
|
import { Boundary } from '../components/Boundary';
|
||||||
import type { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
import type { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
||||||
import { useApi } from '../contexts/ApiContext';
|
import { useApi } from '../contexts/ApiContext';
|
||||||
@ -86,7 +90,7 @@ export type InvenTreeTableProps<T = any> = {
|
|||||||
onRowClick?: (record: T, index: number, event: any) => void;
|
onRowClick?: (record: T, index: number, event: any) => void;
|
||||||
onCellClick?: DataTableCellClickHandler<T>;
|
onCellClick?: DataTableCellClickHandler<T>;
|
||||||
modelType?: ModelType;
|
modelType?: ModelType;
|
||||||
rowStyle?: (record: T, index: number) => any;
|
rowStyle?: (record: T, index: number) => MantineStyleProp | undefined;
|
||||||
modelField?: string;
|
modelField?: string;
|
||||||
onCellContextMenu?: (record: T, event: any) => void;
|
onCellContextMenu?: (record: T, event: any) => void;
|
||||||
minHeight?: number;
|
minHeight?: number;
|
||||||
@ -597,6 +601,10 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
|||||||
[props.onRowClick, props.onCellClick]
|
[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
|
// Callback when a cell is right-clicked
|
||||||
const handleCellContextMenu = ({
|
const handleCellContextMenu = ({
|
||||||
record,
|
record,
|
||||||
@ -612,9 +620,13 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
|||||||
}
|
}
|
||||||
if (props.onCellContextMenu) {
|
if (props.onCellContextMenu) {
|
||||||
return props.onCellContextMenu(record, event);
|
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 ?? '',
|
key: action.title ?? '',
|
||||||
title: action.title ?? '',
|
title: action.title ?? '',
|
||||||
color: action.color,
|
color: action.color,
|
||||||
@ -623,10 +635,25 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
|||||||
hidden: action.hidden,
|
hidden: action.hidden,
|
||||||
disabled: action.disabled
|
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 refresh table if pageSize changes
|
// pagination refresh table if pageSize changes
|
||||||
@ -688,6 +715,14 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
|||||||
updatePageSize
|
updatePageSize
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const supportsCellClick = useMemo(() => {
|
||||||
|
return !!(
|
||||||
|
tableProps.onCellClick ||
|
||||||
|
tableProps.onRowClick ||
|
||||||
|
tableProps.modelType
|
||||||
|
);
|
||||||
|
}, [tableProps.onCellClick, tableProps.onRowClick, tableProps.modelType]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack gap='xs'>
|
<Stack gap='xs'>
|
||||||
@ -724,12 +759,12 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
|||||||
enableSelection ? onSelectedRecordsChange : undefined
|
enableSelection ? onSelectedRecordsChange : undefined
|
||||||
}
|
}
|
||||||
rowExpansion={rowExpansion}
|
rowExpansion={rowExpansion}
|
||||||
rowStyle={tableProps.rowStyle}
|
// rowStyle={rowStyleCallback}
|
||||||
fetching={isFetching}
|
fetching={isFetching}
|
||||||
noRecordsText={missingRecordsText}
|
noRecordsText={missingRecordsText}
|
||||||
records={tableState.records}
|
records={tableState.records}
|
||||||
columns={dataColumns}
|
columns={dataColumns}
|
||||||
onCellClick={handleCellClick}
|
onCellClick={supportsCellClick ? handleCellClick : undefined}
|
||||||
noHeader={tableProps.noHeader ?? false}
|
noHeader={tableProps.noHeader ?? false}
|
||||||
defaultColumnProps={{
|
defaultColumnProps={{
|
||||||
noWrap: true,
|
noWrap: true,
|
||||||
@ -739,7 +774,9 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
|||||||
overflow: 'hidden'
|
overflow: 'hidden'
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
onCellContextMenu={handleCellContextMenu}
|
onCellContextMenu={
|
||||||
|
supportsContextMenu ? handleCellContextMenu : undefined
|
||||||
|
}
|
||||||
{...optionalParams}
|
{...optionalParams}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user