mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
Details image tweaks (#6603)
* Changes for PartThumbTable: - Limit use of custom styling - Better display of images * Fix background color * Use <StylishText> for titles * Cleanup details grid - Use Mantine components - Simplify structure * Fix TableThumbProps
This commit is contained in:
parent
cbd2794a7e
commit
05e67d310a
@ -1,9 +1,11 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
AspectRatio,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Modal,
|
||||
Overlay,
|
||||
Paper,
|
||||
Text,
|
||||
rem,
|
||||
@ -20,6 +22,7 @@ import { InvenTreeIcon } from '../../functions/icons';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { PartThumbTable } from '../../tables/part/PartThumbTable';
|
||||
import { ActionButton } from '../buttons/ActionButton';
|
||||
import { StylishText } from '../items/StylishText';
|
||||
import { ApiImage } from './ApiImage';
|
||||
|
||||
/**
|
||||
@ -58,9 +61,9 @@ const backup_image = '/static/img/blank_image.png';
|
||||
*/
|
||||
const removeModal = (apiPath: string, setImage: (image: string) => void) =>
|
||||
modals.openConfirmModal({
|
||||
title: t`Remove Image`,
|
||||
title: <StylishText size="xl">{t`Remove Image`}</StylishText>,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
<Text>
|
||||
<Trans>Remove the associated image from this item?</Trans>
|
||||
</Text>
|
||||
),
|
||||
@ -245,13 +248,8 @@ function ImageActionButtons({
|
||||
pk: string;
|
||||
setImage: (image: string) => void;
|
||||
}) {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal opened={opened} onClose={close} title={t`Select image`} size="70%">
|
||||
<PartThumbTable pk={pk} close={close} setImage={setImage} />
|
||||
</Modal>
|
||||
{visible && (
|
||||
<Group
|
||||
spacing="xs"
|
||||
@ -259,24 +257,37 @@ function ImageActionButtons({
|
||||
>
|
||||
{actions.selectExisting && (
|
||||
<ActionButton
|
||||
icon={<InvenTreeIcon icon="select_image" />}
|
||||
icon={
|
||||
<InvenTreeIcon
|
||||
icon="select_image"
|
||||
iconProps={{ color: 'white' }}
|
||||
/>
|
||||
}
|
||||
tooltip={t`Select from existing images`}
|
||||
variant="outline"
|
||||
size="lg"
|
||||
tooltipAlignment="top"
|
||||
onClick={open}
|
||||
onClick={() => {
|
||||
modals.open({
|
||||
title: <StylishText size="xl">{t`Select Image`}</StylishText>,
|
||||
size: 'xxl',
|
||||
children: <PartThumbTable pk={pk} setImage={setImage} />
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{actions.uploadFile && (
|
||||
<ActionButton
|
||||
icon={<InvenTreeIcon icon="upload" />}
|
||||
icon={
|
||||
<InvenTreeIcon icon="upload" iconProps={{ color: 'white' }} />
|
||||
}
|
||||
tooltip={t`Upload new image`}
|
||||
variant="outline"
|
||||
size="lg"
|
||||
tooltipAlignment="top"
|
||||
onClick={() => {
|
||||
modals.open({
|
||||
title: t`Upload Image`,
|
||||
title: <StylishText size="xl">{t`Upload Image`}</StylishText>,
|
||||
children: (
|
||||
<UploadModal apiPath={apiPath} setImage={setImage} />
|
||||
)
|
||||
@ -320,19 +331,10 @@ export function DetailsImage(props: DetailImageProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper
|
||||
ref={ref}
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: `${IMAGE_DIMENSION}px`,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1}>
|
||||
<>
|
||||
<ApiImage
|
||||
src={img}
|
||||
style={{ zIndex: 1 }}
|
||||
height={IMAGE_DIMENSION}
|
||||
width={IMAGE_DIMENSION}
|
||||
onClick={() => {
|
||||
@ -342,7 +344,8 @@ export function DetailsImage(props: DetailImageProps) {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{permissions.hasChangeRole(props.appRole) && (
|
||||
{permissions.hasChangeRole(props.appRole) && hovered && (
|
||||
<Overlay color="black" opacity={0.8}>
|
||||
<ImageActionButtons
|
||||
visible={hovered}
|
||||
actions={props.imageActions}
|
||||
@ -351,8 +354,10 @@ export function DetailsImage(props: DetailImageProps) {
|
||||
pk={props.pk}
|
||||
setImage={setAndRefresh}
|
||||
/>
|
||||
</Overlay>
|
||||
)}
|
||||
</Paper>
|
||||
</>
|
||||
</AspectRatio>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
Badge,
|
||||
CopyButton,
|
||||
Group,
|
||||
Paper,
|
||||
Skeleton,
|
||||
Table,
|
||||
Text,
|
||||
@ -437,7 +438,7 @@ export function DetailsTable({
|
||||
partIcons?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Group>
|
||||
<Paper p="xs" withBorder radius="xs">
|
||||
<Table striped>
|
||||
<tbody>
|
||||
{partIcons && (
|
||||
@ -475,6 +476,6 @@ export function DetailsTable({
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</Group>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Paper } from '@mantine/core';
|
||||
import { Grid, Group, Paper, SimpleGrid } from '@mantine/core';
|
||||
|
||||
import {
|
||||
DetailImageButtonProps,
|
||||
@ -50,13 +50,11 @@ export function ItemDetails({
|
||||
partModel: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Paper style={{ display: 'flex', gap: '20px', flexWrap: 'wrap' }}>
|
||||
<Paper
|
||||
withBorder
|
||||
style={{ flexBasis: '49%', display: 'flex', gap: '10px' }}
|
||||
>
|
||||
<Paper p="xs">
|
||||
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
||||
<Grid>
|
||||
{fields.image && (
|
||||
<div style={{ flexGrow: '0' }}>
|
||||
<Grid.Col span={4}>
|
||||
<DetailsImage
|
||||
appRole={appRole}
|
||||
imageActions={fields.image.imageActions}
|
||||
@ -65,33 +63,26 @@ export function ItemDetails({
|
||||
refresh={refresh}
|
||||
pk={params.pk}
|
||||
/>
|
||||
</div>
|
||||
</Grid.Col>
|
||||
)}
|
||||
<Grid.Col span={8}>
|
||||
{fields.left && (
|
||||
<div style={{ flexGrow: '1' }}>
|
||||
<DetailsTable
|
||||
item={params}
|
||||
fields={fields.left}
|
||||
partIcons={partModel}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Paper>
|
||||
{fields.right && (
|
||||
<Paper style={{ flexBasis: '49%' }} withBorder>
|
||||
<DetailsTable item={params} fields={fields.right} />
|
||||
</Paper>
|
||||
)}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
{fields.right && <DetailsTable item={params} fields={fields.right} />}
|
||||
{fields.bottom_left && (
|
||||
<Paper style={{ flexBasis: '49%' }} withBorder>
|
||||
<DetailsTable item={params} fields={fields.bottom_left} />
|
||||
</Paper>
|
||||
)}
|
||||
{fields.bottom_right && (
|
||||
<Paper style={{ flexBasis: '49%' }} withBorder>
|
||||
<DetailsTable item={params} fields={fields.bottom_right} />
|
||||
</Paper>
|
||||
)}
|
||||
</SimpleGrid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,18 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Button, Paper, Skeleton, Text, TextInput } from '@mantine/core';
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
AspectRatio,
|
||||
Button,
|
||||
Divider,
|
||||
Group,
|
||||
Paper,
|
||||
SimpleGrid,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput
|
||||
} from '@mantine/core';
|
||||
import { useHover } from '@mantine/hooks';
|
||||
import { modals } from '@mantine/modals';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import React, { Suspense, useEffect, useState } from 'react';
|
||||
|
||||
@ -17,7 +29,6 @@ export type ThumbTableProps = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
search?: string;
|
||||
close: () => void;
|
||||
setImage: (image: string) => void;
|
||||
};
|
||||
|
||||
@ -62,29 +73,19 @@ function PartThumbComponent({ selected, element, selectImage }: ThumbProps) {
|
||||
return (
|
||||
<Paper
|
||||
withBorder
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
padding: '5px',
|
||||
display: 'flex',
|
||||
flex: '0 1 150px',
|
||||
flexFlow: 'column wrap',
|
||||
placeContent: 'center space-between'
|
||||
}}
|
||||
style={{ backgroundColor: color }}
|
||||
p="sm"
|
||||
ref={ref}
|
||||
onClick={() => selectImage(element.image)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexGrow: 1
|
||||
}}
|
||||
>
|
||||
<Stack justify="space-between">
|
||||
<AspectRatio ratio={1}>
|
||||
<Thumbnail size={120} src={src} align="center"></Thumbnail>
|
||||
</div>
|
||||
<Text style={{ alignSelf: 'center', overflowWrap: 'anywhere' }}>
|
||||
</AspectRatio>
|
||||
<Text size="xs">
|
||||
{element.image.split('/')[1]} ({element.count})
|
||||
</Text>
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@ -95,7 +96,6 @@ function PartThumbComponent({ selected, element, selectImage }: ThumbProps) {
|
||||
async function setNewImage(
|
||||
image: string | null,
|
||||
pk: string,
|
||||
close: () => void,
|
||||
setImage: (image: string) => void
|
||||
) {
|
||||
// No need to do anything if no image is selected
|
||||
@ -110,7 +110,7 @@ async function setNewImage(
|
||||
// Update image component and close modal if update was successful
|
||||
if (response.data.image.includes(image)) {
|
||||
setImage(response.data.image);
|
||||
close();
|
||||
modals.closeAll();
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,11 +118,10 @@ async function setNewImage(
|
||||
* Renders a "table" of thumbnails
|
||||
*/
|
||||
export function PartThumbTable({
|
||||
limit = 25,
|
||||
limit = 24,
|
||||
offset = 0,
|
||||
search = '',
|
||||
pk,
|
||||
close,
|
||||
setImage
|
||||
}: ThumbTableProps) {
|
||||
const [img, selectImage] = useState<string | null>(null);
|
||||
@ -155,24 +154,21 @@ export function PartThumbTable({
|
||||
return (
|
||||
<>
|
||||
<Suspense>
|
||||
<Paper
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'stretch',
|
||||
placeContent: 'stretch center',
|
||||
flexWrap: 'wrap',
|
||||
gap: '10px'
|
||||
}}
|
||||
>
|
||||
<Divider />
|
||||
<Paper p="sm">
|
||||
<>
|
||||
<SimpleGrid cols={8}>
|
||||
{!thumbQuery.isFetching
|
||||
? thumbQuery.data?.data.map((data: ImageElement, index: number) => (
|
||||
? thumbQuery.data?.data.map(
|
||||
(data: ImageElement, index: number) => (
|
||||
<PartThumbComponent
|
||||
element={data}
|
||||
key={index}
|
||||
selected={img}
|
||||
selectImage={selectImage}
|
||||
/>
|
||||
))
|
||||
)
|
||||
)
|
||||
: [...Array(limit)].map((elem, idx) => (
|
||||
<Skeleton
|
||||
height={150}
|
||||
@ -182,22 +178,14 @@ export function PartThumbTable({
|
||||
style={{ padding: '5px' }}
|
||||
/>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</>
|
||||
</Paper>
|
||||
</Suspense>
|
||||
<Paper
|
||||
style={{
|
||||
position: 'sticky',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: '60px',
|
||||
zIndex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
}}
|
||||
>
|
||||
|
||||
<Divider />
|
||||
<Paper p="sm">
|
||||
<Group position="apart">
|
||||
<TextInput
|
||||
placeholder={t`Search...`}
|
||||
onChange={(event) => {
|
||||
@ -206,10 +194,11 @@ export function PartThumbTable({
|
||||
/>
|
||||
<Button
|
||||
disabled={!img}
|
||||
onClick={() => setNewImage(img, pk, close, setImage)}
|
||||
onClick={() => setNewImage(img, pk, setImage)}
|
||||
>
|
||||
Submit
|
||||
<Trans>Select</Trans>
|
||||
</Button>
|
||||
</Group>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user