mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-02 03:30:54 +00:00
[UI] Hover image (#9907)
* Add "hover image" for part display in tables * Refactor company column
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Anchor, Group } from '@mantine/core';
|
||||
import { Anchor, Group, HoverCard, Image } from '@mantine/core';
|
||||
import { type ReactNode, useMemo } from 'react';
|
||||
|
||||
import { ApiImage } from './ApiImage';
|
||||
@ -13,7 +13,9 @@ export function Thumbnail({
|
||||
size = 20,
|
||||
link,
|
||||
text,
|
||||
align
|
||||
align,
|
||||
hover,
|
||||
hoverSize = 128
|
||||
}: Readonly<{
|
||||
src?: string;
|
||||
alt?: string;
|
||||
@ -21,6 +23,8 @@ export function Thumbnail({
|
||||
text?: ReactNode;
|
||||
align?: string;
|
||||
link?: string;
|
||||
hover?: boolean;
|
||||
hoverSize?: number;
|
||||
}>) {
|
||||
const backup_image = '/static/img/blank_image.png';
|
||||
|
||||
@ -37,6 +41,14 @@ export function Thumbnail({
|
||||
}, [link, text]);
|
||||
|
||||
return (
|
||||
<HoverCard
|
||||
disabled={!hover}
|
||||
withinPortal
|
||||
position='left'
|
||||
shadow='xs'
|
||||
closeDelay={100}
|
||||
>
|
||||
<HoverCard.Target>
|
||||
<Group align={align ?? 'left'} gap='xs' wrap='nowrap'>
|
||||
<ApiImage
|
||||
src={src || backup_image}
|
||||
@ -48,5 +60,16 @@ export function Thumbnail({
|
||||
/>
|
||||
{inner}
|
||||
</Group>
|
||||
</HoverCard.Target>
|
||||
<HoverCard.Dropdown>
|
||||
<Image
|
||||
src={src || backup_image}
|
||||
alt={alt}
|
||||
w={hoverSize}
|
||||
fit='contain'
|
||||
style={{ maxHeight: hoverSize }}
|
||||
/>
|
||||
</HoverCard.Dropdown>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ export function PartColumn({
|
||||
<Thumbnail
|
||||
src={part?.thumbnail ?? part?.image}
|
||||
text={full_name ? part?.full_name : part?.name}
|
||||
hover
|
||||
/>
|
||||
<Group justify='flex-end' wrap='nowrap' gap='xs'>
|
||||
{part?.active == false && (
|
||||
@ -55,6 +56,26 @@ export function PartColumn({
|
||||
);
|
||||
}
|
||||
|
||||
export function CompanyColumn({
|
||||
company
|
||||
}: {
|
||||
company: any;
|
||||
}) {
|
||||
return company ? (
|
||||
<Group gap='xs' wrap='nowrap'>
|
||||
<Thumbnail
|
||||
src={company.thumbnail ?? company.image ?? ''}
|
||||
alt={company.name}
|
||||
size={24}
|
||||
hover
|
||||
/>
|
||||
<Text>{company.name}</Text>
|
||||
</Group>
|
||||
) : (
|
||||
<Skeleton />
|
||||
);
|
||||
}
|
||||
|
||||
export function LocationColumn(props: TableColumnProps): TableColumn {
|
||||
return {
|
||||
accessor: 'location',
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Text } from '@mantine/core';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
@ -10,7 +9,6 @@ import { apiUrl } from '@lib/functions/Api';
|
||||
import { navigateToLink } from '@lib/functions/Navigation';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||
import { companyFields } from '../../forms/CompanyForms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
@ -18,7 +16,11 @@ import {
|
||||
} from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { BooleanColumn, DescriptionColumn } from '../ColumnRenderers';
|
||||
import {
|
||||
BooleanColumn,
|
||||
CompanyColumn,
|
||||
DescriptionColumn
|
||||
} from '../ColumnRenderers';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { type RowAction, RowEditAction } from '../RowActions';
|
||||
|
||||
@ -45,16 +47,7 @@ export function CompanyTable({
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
render: (record: any) => {
|
||||
return (
|
||||
<Group gap='xs' wrap='nowrap'>
|
||||
<Thumbnail
|
||||
src={record.thumbnail ?? record.image ?? ''}
|
||||
alt={record.name}
|
||||
size={24}
|
||||
/>
|
||||
<Text>{record.name}</Text>
|
||||
</Group>
|
||||
);
|
||||
return <CompanyColumn company={record} />;
|
||||
}
|
||||
},
|
||||
DescriptionColumn({}),
|
||||
|
@ -6,7 +6,6 @@ import { ModelType } from '@lib/enums/ModelType';
|
||||
import { UserRoles } from '@lib/enums/Roles';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||
import { useManufacturerPartFields } from '../../forms/CompanyForms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
@ -16,7 +15,12 @@ import {
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import type { TableColumn } from '../Column';
|
||||
import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers';
|
||||
import {
|
||||
CompanyColumn,
|
||||
DescriptionColumn,
|
||||
LinkColumn,
|
||||
PartColumn
|
||||
} from '../ColumnRenderers';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { type RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
|
||||
|
||||
@ -42,16 +46,9 @@ export function ManufacturerPartTable({
|
||||
{
|
||||
accessor: 'manufacturer',
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
const manufacturer = record?.manufacturer_detail ?? {};
|
||||
|
||||
return (
|
||||
<Thumbnail
|
||||
src={manufacturer?.thumbnail ?? manufacturer.image}
|
||||
text={manufacturer.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record?.manufacturer_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'MPN',
|
||||
|
@ -7,13 +7,13 @@ import { UserRoles } from '@lib/enums/Roles';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import {
|
||||
CompanyColumn,
|
||||
CompletionDateColumn,
|
||||
CreatedByColumn,
|
||||
CreationDateColumn,
|
||||
@ -106,17 +106,9 @@ export function PurchaseOrderTable({
|
||||
accessor: 'supplier__name',
|
||||
title: t`Supplier`,
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
const supplier = record.supplier_detail ?? {};
|
||||
|
||||
return (
|
||||
<Thumbnail
|
||||
src={supplier?.image}
|
||||
alt={supplier.name}
|
||||
text={supplier.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record.supplier_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'supplier_reference'
|
||||
|
@ -8,7 +8,6 @@ import { UserRoles } from '@lib/enums/Roles';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||
import { useSupplierPartFields } from '../../forms/CompanyForms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
@ -20,6 +19,7 @@ import { useUserState } from '../../states/UserState';
|
||||
import type { TableColumn } from '../Column';
|
||||
import {
|
||||
BooleanColumn,
|
||||
CompanyColumn,
|
||||
DescriptionColumn,
|
||||
LinkColumn,
|
||||
NoteColumn,
|
||||
@ -53,18 +53,9 @@ export function SupplierPartTable({
|
||||
{
|
||||
accessor: 'supplier',
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
const supplier = record?.supplier_detail ?? {};
|
||||
|
||||
return supplier?.pk ? (
|
||||
<Thumbnail
|
||||
src={supplier?.thumbnail ?? supplier.image}
|
||||
text={supplier.name}
|
||||
/>
|
||||
) : (
|
||||
'-'
|
||||
);
|
||||
}
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record?.supplier_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'SKU',
|
||||
@ -76,18 +67,9 @@ export function SupplierPartTable({
|
||||
accessor: 'manufacturer',
|
||||
title: t`Manufacturer`,
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
const manufacturer = record?.manufacturer_detail ?? {};
|
||||
|
||||
return manufacturer?.pk ? (
|
||||
<Thumbnail
|
||||
src={manufacturer?.thumbnail ?? manufacturer.image}
|
||||
text={manufacturer.name}
|
||||
/>
|
||||
) : (
|
||||
'-'
|
||||
);
|
||||
}
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record?.manufacturer_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'MPN',
|
||||
|
@ -9,7 +9,6 @@ import { apiUrl } from '@lib/functions/Api';
|
||||
import { getDetailUrl } from '@lib/functions/Navigation';
|
||||
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
@ -19,6 +18,7 @@ import {
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import type { TableColumn } from '../Column';
|
||||
import { CompanyColumn } from '../ColumnRenderers';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { type RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
|
||||
|
||||
@ -36,21 +36,9 @@ export function SupplierPriceBreakColumns(): TableColumn[] {
|
||||
title: t`Supplier`,
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
render: (record: any) => {
|
||||
return (
|
||||
<Group gap='xs' wrap='nowrap'>
|
||||
<Thumbnail
|
||||
src={
|
||||
record?.supplier_detail?.thumbnail ??
|
||||
record?.supplier_detail?.image
|
||||
}
|
||||
alt={record?.supplier_detail?.name}
|
||||
size={24}
|
||||
/>
|
||||
<Text>{record.supplier_detail?.name}</Text>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record.supplier_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'part_detail.SKU',
|
||||
|
@ -7,13 +7,13 @@ import { UserRoles } from '@lib/enums/Roles';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
import { useReturnOrderFields } from '../../forms/ReturnOrderForms';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import {
|
||||
CompanyColumn,
|
||||
CompletionDateColumn,
|
||||
CreatedByColumn,
|
||||
CreationDateColumn,
|
||||
@ -111,17 +111,9 @@ export function ReturnOrderTable({
|
||||
accessor: 'customer__name',
|
||||
title: t`Customer`,
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
const customer = record.customer_detail ?? {};
|
||||
|
||||
return (
|
||||
<Thumbnail
|
||||
src={customer?.image}
|
||||
alt={customer.name}
|
||||
text={customer.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record.customer_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'customer_reference'
|
||||
|
@ -7,7 +7,6 @@ import { UserRoles } from '@lib/enums/Roles';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||
import { ProgressBar } from '../../components/items/ProgressBar';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
import { useSalesOrderFields } from '../../forms/SalesOrderForms';
|
||||
@ -15,6 +14,7 @@ import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import {
|
||||
CompanyColumn,
|
||||
CreatedByColumn,
|
||||
CreationDateColumn,
|
||||
DescriptionColumn,
|
||||
@ -136,17 +136,9 @@ export function SalesOrderTable({
|
||||
accessor: 'customer__name',
|
||||
title: t`Customer`,
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
const customer = record.customer_detail ?? {};
|
||||
|
||||
return (
|
||||
<Thumbnail
|
||||
src={customer?.image}
|
||||
alt={customer.name}
|
||||
text={customer.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record.customer_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'customer_reference',
|
||||
|
Reference in New Issue
Block a user