minor redesign inventory + add functions
This commit is contained in:
@ -1,16 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Grid,
|
||||
Button,
|
||||
FormControl,
|
||||
Select,
|
||||
MenuItem,
|
||||
InputLabel,
|
||||
Tooltip,
|
||||
Paper,
|
||||
} from '@mui/material';
|
||||
import { Box, Typography, Grid, Button, Paper } from '@mui/material';
|
||||
|
||||
import { FullScreenLoader } from '../components/FullScreenLoader';
|
||||
import { translateServer } from '../utils/serverTranslator';
|
||||
@ -20,6 +9,8 @@ import {
|
||||
type InventoryRawItem,
|
||||
type InventoryItemsResponse,
|
||||
} from '../api';
|
||||
import CustomTooltip from '../components/Notifications/CustomTooltip';
|
||||
import { getPlayerServer } from '../utils/playerOnlineCheck';
|
||||
|
||||
const KNOWN_SERVER_IPS = [
|
||||
'minecraft.hub.popa-popa.ru',
|
||||
@ -32,22 +23,75 @@ function stripMinecraftColors(text?: string | null): string {
|
||||
return text.replace(/§[0-9A-FK-ORa-fk-or]/g, '');
|
||||
}
|
||||
|
||||
const Glass = ({ children }: { children: React.ReactNode }) => (
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: '1.2vw',
|
||||
overflow: 'hidden',
|
||||
background:
|
||||
'radial-gradient(circle at 10% 10%, rgba(242,113,33,0.14), transparent 55%), radial-gradient(circle at 90% 20%, rgba(233,64,205,0.12), transparent 55%), rgba(10,10,20,0.86)',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
boxShadow: '0 1.2vw 3.2vw rgba(0,0,0,0.55)',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: '1.8vw' }}>{children}</Box>
|
||||
</Paper>
|
||||
);
|
||||
const CARD_BG =
|
||||
'radial-gradient(circle at 10% 10%, rgba(242,113,33,0.14), transparent 55%), radial-gradient(circle at 90% 20%, rgba(233,64,205,0.12), transparent 55%), rgba(10,10,20,0.86)';
|
||||
|
||||
const CardFacePaperSx = {
|
||||
borderRadius: '1.2vw',
|
||||
background: CARD_BG,
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
boxShadow: '0 1.2vw 3.2vw rgba(0,0,0,0.55)',
|
||||
color: 'white',
|
||||
} as const;
|
||||
|
||||
function readLauncherConfig(): any {
|
||||
try {
|
||||
const raw = localStorage.getItem('launcher_config');
|
||||
return raw ? JSON.parse(raw) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function writeLauncherConfig(next: any) {
|
||||
localStorage.setItem('launcher_config', JSON.stringify(next));
|
||||
}
|
||||
|
||||
function getLayout(username: string, serverIp: string): Record<string, number> {
|
||||
const cfg = readLauncherConfig();
|
||||
return cfg?.inventory_layout?.[username]?.[serverIp] ?? {};
|
||||
}
|
||||
|
||||
function setLayout(username: string, serverIp: string, layout: Record<string, number>) {
|
||||
const cfg = readLauncherConfig();
|
||||
cfg.inventory_layout ??= {};
|
||||
cfg.inventory_layout[username] ??= {};
|
||||
cfg.inventory_layout[username][serverIp] = layout;
|
||||
writeLauncherConfig(cfg);
|
||||
}
|
||||
|
||||
function buildSlots(
|
||||
items: InventoryRawItem[],
|
||||
layout: Record<string, number>,
|
||||
size = 28,
|
||||
): (InventoryRawItem | null)[] {
|
||||
const slots: (InventoryRawItem | null)[] = Array.from({ length: size }, () => null);
|
||||
|
||||
const byId = new Map(items.map((it) => [it.id, it]));
|
||||
const used = new Set<string>();
|
||||
|
||||
// 1) ставим туда, куда сохранено
|
||||
for (const [id, idx] of Object.entries(layout)) {
|
||||
const i = Number(idx);
|
||||
const it = byId.get(id);
|
||||
if (!it) continue;
|
||||
if (Number.isFinite(i) && i >= 0 && i < size && !slots[i]) {
|
||||
slots[i] = it;
|
||||
used.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) остальные — в первые пустые
|
||||
for (const it of items) {
|
||||
if (used.has(it.id)) continue;
|
||||
const empty = slots.findIndex((x) => x === null);
|
||||
if (empty === -1) break;
|
||||
slots[empty] = it;
|
||||
used.add(it.id);
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
export default function Inventory() {
|
||||
const [username, setUsername] = useState('');
|
||||
@ -60,7 +104,72 @@ export default function Inventory() {
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pages, setPages] = useState(1);
|
||||
const [withdrawingIds, setWithdrawingIds] = useState<string[]>([]);
|
||||
const isServerSelectVisible = availableServers.length > 1;
|
||||
const [hoveredId, setHoveredId] = useState<string | null>(null);
|
||||
|
||||
const [isOnline, setIsOnline] = useState(false);
|
||||
const [playerServer, setPlayerServer] = useState<string | null>(null);
|
||||
const [checkingOnline, setCheckingOnline] = useState(false);
|
||||
|
||||
const [draggedItemId, setDraggedItemId] = useState<string | null>(null);
|
||||
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
|
||||
const [dragPos, setDragPos] = useState<{ x: number; y: number } | null>(null);
|
||||
|
||||
type SlotItem = InventoryRawItem | null;
|
||||
|
||||
const [slots, setSlots] = useState<SlotItem[]>(() => Array.from({ length: 28 }, () => null));
|
||||
const [draggedFromIndex, setDraggedFromIndex] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleMove = (e: MouseEvent) => {
|
||||
if (!draggedItemId) return;
|
||||
setDragPos({ x: e.clientX, y: e.clientY });
|
||||
};
|
||||
|
||||
const handleUp = () => {
|
||||
if (
|
||||
draggedItemId &&
|
||||
draggedFromIndex !== null &&
|
||||
dragOverIndex !== null &&
|
||||
draggedFromIndex !== dragOverIndex
|
||||
) {
|
||||
setSlots((prev) => {
|
||||
const next = [...prev];
|
||||
const moving = next[draggedFromIndex];
|
||||
if (!moving) return prev;
|
||||
|
||||
// ✅ swap или move в пустоту
|
||||
const target = next[dragOverIndex];
|
||||
next[dragOverIndex] = moving;
|
||||
next[draggedFromIndex] = target ?? null;
|
||||
|
||||
// ✅ сохраняем layout (позиции предметов)
|
||||
const layout: Record<string, number> = {};
|
||||
next.forEach((it, idx) => {
|
||||
if (it) layout[it.id] = idx;
|
||||
});
|
||||
|
||||
if (username && selectedServerIp) {
|
||||
setLayout(username, selectedServerIp, layout);
|
||||
}
|
||||
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
setDraggedItemId(null);
|
||||
setDraggedFromIndex(null);
|
||||
setDragOverIndex(null);
|
||||
setDragPos(null);
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', handleMove);
|
||||
window.addEventListener('mouseup', handleUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleMove);
|
||||
window.removeEventListener('mouseup', handleUp);
|
||||
};
|
||||
}, [draggedItemId, dragOverIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
const savedConfig = localStorage.getItem('launcher_config');
|
||||
@ -82,19 +191,24 @@ export default function Inventory() {
|
||||
}),
|
||||
);
|
||||
|
||||
const servers = checks
|
||||
.filter((r): r is PromiseFulfilledResult<{ ip: string; has: boolean }> => r.status === 'fulfilled')
|
||||
return checks
|
||||
.filter(
|
||||
(r): r is PromiseFulfilledResult<{ ip: string; has: boolean }> => r.status === 'fulfilled',
|
||||
)
|
||||
.filter((r) => r.value.has)
|
||||
.map((r) => r.value.ip);
|
||||
|
||||
return servers;
|
||||
};
|
||||
|
||||
const loadInventory = async (u: string, ip: string, p: number) => {
|
||||
const res: InventoryItemsResponse = await fetchInventoryItems(u, ip, p, limit);
|
||||
const list = res.items || [];
|
||||
|
||||
setItems(res.items || []);
|
||||
setTotal(res.total ?? 0);
|
||||
setPages(Math.max(1, Math.ceil((res.total ?? 0) / (res.limit ?? limit))));
|
||||
|
||||
const layout = getLayout(u, ip);
|
||||
setSlots(buildSlots(list, layout, 28));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -164,11 +278,21 @@ export default function Inventory() {
|
||||
};
|
||||
}, [selectedServerIp]);
|
||||
|
||||
const withWithdrawing = async (id: string, fn: () => Promise<void>) => {
|
||||
setWithdrawingIds((prev) => [...prev, id]);
|
||||
try {
|
||||
await fn();
|
||||
} finally {
|
||||
setWithdrawingIds((prev) => prev.filter((x) => x !== id));
|
||||
}
|
||||
};
|
||||
|
||||
const handleWithdraw = async (item: InventoryRawItem) => {
|
||||
if (!username || !selectedServerIp) return;
|
||||
|
||||
// сервер в UI может не совпадать — оставим защиту
|
||||
if (selectedServerIp !== item.server_ip) {
|
||||
alert("Ошибка! Вы не на том сервере для выдачи этого предмета.");
|
||||
alert('Ошибка! Вы не на том сервере для выдачи этого предмета.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -181,24 +305,48 @@ export default function Inventory() {
|
||||
});
|
||||
|
||||
setItems((prevItems) => prevItems.filter((prevItem) => prevItem.id !== item.id));
|
||||
|
||||
setSlots((prev) => {
|
||||
const next = prev.map((x) => (x?.id === item.id ? null : x));
|
||||
|
||||
const layout: Record<string, number> = {};
|
||||
next.forEach((it, idx) => {
|
||||
if (it) layout[it.id] = idx;
|
||||
});
|
||||
|
||||
setLayout(username, selectedServerIp, layout);
|
||||
return next;
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Ошибка при выводе предмета:", e);
|
||||
console.error('Ошибка при выводе предмета:', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const withWithdrawing = async (id: string, fn: () => Promise<void>) => {
|
||||
setWithdrawingIds((prev) => [...prev, id]);
|
||||
const checkPlayerStatus = async () => {
|
||||
if (!username) return;
|
||||
|
||||
setCheckingOnline(true);
|
||||
try {
|
||||
await fn();
|
||||
const res = await getPlayerServer(username);
|
||||
setIsOnline(!!res?.online);
|
||||
setPlayerServer(res?.server?.ip ?? null);
|
||||
} catch (e) {
|
||||
console.error('Ошибка проверки онлайна:', e);
|
||||
setIsOnline(false);
|
||||
setPlayerServer(null);
|
||||
} finally {
|
||||
setWithdrawingIds((prev) => prev.filter((x) => x !== id));
|
||||
setCheckingOnline(false);
|
||||
}
|
||||
};
|
||||
|
||||
const headerServerName = selectedServerIp
|
||||
? translateServer(`Server ${selectedServerIp}`)
|
||||
: '';
|
||||
useEffect(() => {
|
||||
if (!username) return;
|
||||
checkPlayerStatus();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [username]);
|
||||
|
||||
const headerServerName = selectedServerIp ? translateServer(`Server ${selectedServerIp}`) : '';
|
||||
|
||||
if (!username) {
|
||||
return (
|
||||
@ -253,42 +401,51 @@ export default function Inventory() {
|
||||
>
|
||||
{loading && <FullScreenLoader fullScreen={false} message="Загрузка инвентаря..." />}
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, flexWrap: 'wrap', justifyContent: 'space-evenly', flexDirection: 'row-reverse' }}>
|
||||
{/* Пагинация */}
|
||||
{/* ШАПКА + ПАГИНАЦИЯ */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 2,
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-evenly',
|
||||
flexDirection: 'row-reverse',
|
||||
}}
|
||||
>
|
||||
{!!selectedServerIp && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={!canPrev || loading}
|
||||
onClick={handlePrev}
|
||||
sx={{
|
||||
variant="outlined"
|
||||
disabled={!canPrev || loading}
|
||||
onClick={handlePrev}
|
||||
sx={{
|
||||
borderRadius: '2.5vw',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
color: 'white',
|
||||
border: '1px solid rgba(255,255,255,0.15)',
|
||||
}}
|
||||
}}
|
||||
>
|
||||
Назад
|
||||
Назад
|
||||
</Button>
|
||||
|
||||
<Typography sx={{ color: 'rgba(255,255,255,0.7)' }}>
|
||||
Страница {page} / {pages} • Всего: {total}
|
||||
Страница {page} / {pages} • Всего: {total}
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={!canNext || loading}
|
||||
onClick={handleNext}
|
||||
sx={{
|
||||
variant="outlined"
|
||||
disabled={!canNext || loading}
|
||||
onClick={handleNext}
|
||||
sx={{
|
||||
borderRadius: '2.5vw',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
color: 'white',
|
||||
border: '1px solid rgba(255,255,255,0.15)',
|
||||
}}
|
||||
}}
|
||||
>
|
||||
Вперёд
|
||||
Вперёд
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Typography
|
||||
@ -303,57 +460,284 @@ export default function Inventory() {
|
||||
>
|
||||
Инвентарь {headerServerName ? `— ${headerServerName}` : ''}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* GRID */}
|
||||
<Grid container spacing={2}>
|
||||
{Array.from({ length: 28 }).map((_, index) => {
|
||||
const item = items[index];
|
||||
const item = slots[index];
|
||||
|
||||
// ПУСТАЯ ЯЧЕЙКА
|
||||
if (!item) {
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
xs={3}
|
||||
key={index}
|
||||
onMouseEnter={() => {
|
||||
if (draggedItemId) setDragOverIndex(index);
|
||||
}}
|
||||
sx={{
|
||||
outline:
|
||||
draggedItemId && dragOverIndex === index
|
||||
? '2px dashed rgba(255,255,255,0.4)'
|
||||
: 'none',
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
...CardFacePaperSx,
|
||||
overflow: 'hidden',
|
||||
width: '12vw',
|
||||
height: '12vw',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: 'rgba(255,255,255,0.5)',
|
||||
}}
|
||||
>
|
||||
<Typography>Пусто</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const displayNameRaw =
|
||||
item.item_data?.meta?.display_name ?? item.item_data?.material ?? 'Предмет';
|
||||
const displayName = stripMinecraftColors(displayNameRaw);
|
||||
|
||||
const amount =
|
||||
(item as any)?.amount ??
|
||||
(item as any)?.item_data?.amount ??
|
||||
(item as any)?.item_data?.meta?.amount ??
|
||||
1;
|
||||
|
||||
const isHovered = hoveredId === item.id;
|
||||
const isWithdrawing = withdrawingIds.includes(item.id);
|
||||
|
||||
// ✅ проверка: игрок реально онлайн на нужном сервере
|
||||
const isOnRightServer = isOnline && playerServer === item.server_ip;
|
||||
const canWithdraw = isOnRightServer && !loading && !checkingOnline && !isWithdrawing;
|
||||
|
||||
const texture = item.item_data?.material
|
||||
? `https://cdn.minecraft.popa-popa.ru/textures/${item.item_data.material.toLowerCase()}.png`
|
||||
: '';
|
||||
|
||||
return (
|
||||
<Grid item xs={3} key={index}>
|
||||
<Glass>
|
||||
<Grid
|
||||
item
|
||||
xs={3}
|
||||
key={item.id}
|
||||
onMouseEnter={() => {
|
||||
if (draggedItemId) setDragOverIndex(index);
|
||||
}}
|
||||
sx={{
|
||||
outline:
|
||||
draggedItemId && dragOverIndex === index
|
||||
? '2px dashed rgba(255,255,255,0.4)'
|
||||
: 'none',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
perspective: '1200px',
|
||||
cursor: draggedItemId === item.id ? 'grabbing' : 'grab',
|
||||
opacity: draggedItemId === item.id ? 0.4 : 1,
|
||||
}}
|
||||
onMouseEnter={() => setHoveredId(item.id)}
|
||||
onMouseLeave={() => setHoveredId(null)}
|
||||
onMouseDown={(e) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (target.closest('button')) return;
|
||||
|
||||
e.preventDefault();
|
||||
setDraggedItemId(item.id);
|
||||
setDraggedFromIndex(index);
|
||||
setDragPos({ x: e.clientX, y: e.clientY });
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '8vw',
|
||||
height: '8vw',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
borderRadius: '5px',
|
||||
boxShadow: item ? '0 10px 40px rgba(0,0,0,0.3)' : 'none',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.1)',
|
||||
cursor: item ? 'pointer' : 'default',
|
||||
},
|
||||
transition: 'all 0.25s ease',
|
||||
width: '12vw',
|
||||
height: '12vw', // фиксированная высота = Grid не дергается
|
||||
transformStyle: 'preserve-3d',
|
||||
transition: 'transform 0.5s cubic-bezier(0.4, 0.2, 0.2, 1)',
|
||||
transform: isHovered ? 'rotateY(180deg)' : 'rotateY(0deg)',
|
||||
}}
|
||||
onClick={() => item && handleWithdraw(item)}
|
||||
>
|
||||
{item ? (
|
||||
<Tooltip
|
||||
title={`${item.item_data?.meta?.display_name}\n${item.item_data?.material}`}
|
||||
placement="top"
|
||||
{/* FRONT */}
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
...CardFacePaperSx,
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
backfaceVisibility: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="img"
|
||||
src={texture}
|
||||
sx={{
|
||||
width: '5vw',
|
||||
height: '5vw',
|
||||
objectFit: 'contain',
|
||||
imageRendering: 'pixelated',
|
||||
userSelect: 'none',
|
||||
transition: 'transform 0.25s ease',
|
||||
transform: isHovered ? 'scale(1.05)' : 'scale(1)',
|
||||
}}
|
||||
draggable={false}
|
||||
alt={displayName}
|
||||
/>
|
||||
</Paper>
|
||||
|
||||
{/* BACK */}
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
...CardFacePaperSx,
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
backfaceVisibility: 'hidden',
|
||||
transform: 'rotateY(180deg)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
gap: '0.8vw',
|
||||
px: '1.1vw',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.8vw',
|
||||
lineHeight: 1.2,
|
||||
textAlign: 'center',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="img"
|
||||
src={`https://cdn.minecraft.popa-popa.ru/textures/${item.item_data?.material.toLowerCase()}.png`}
|
||||
sx={{
|
||||
width: '60%',
|
||||
height: '60%',
|
||||
objectFit: 'contain',
|
||||
imageRendering: 'pixelated',
|
||||
{displayName}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '0.7vw',
|
||||
textAlign: 'center',
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
}}
|
||||
>
|
||||
Кол-во: {amount}
|
||||
</Typography>
|
||||
|
||||
{!isOnRightServer ? (
|
||||
<CustomTooltip
|
||||
essential
|
||||
title={
|
||||
!isOnline
|
||||
? 'Вы должны быть онлайн на сервере'
|
||||
: `Перейдите на сервер ${item.server_ip}`
|
||||
}
|
||||
placement="top"
|
||||
arrow
|
||||
>
|
||||
<span>
|
||||
<Button
|
||||
disabled
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
sx={{
|
||||
fontSize: '0.8vw',
|
||||
borderRadius: '2vw',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
border: '1px solid rgba(255,255,255,0.15)',
|
||||
color: 'rgba(255,255,255,0.5)',
|
||||
}}
|
||||
>
|
||||
Выдать
|
||||
</Button>
|
||||
</span>
|
||||
</CustomTooltip>
|
||||
) : (
|
||||
<Button
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
disabled={!canWithdraw}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleWithdraw(item);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Typography sx={{ color: 'rgba(255,255,255,0.5)' }}>Пусто</Typography>
|
||||
)}
|
||||
sx={{
|
||||
fontSize: '0.8vw',
|
||||
borderRadius: '2vw',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
border: '1px solid rgba(255,255,255,0.15)',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
{isWithdrawing ? 'Выдача...' : 'Выдать'}
|
||||
</Button>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
</Glass>
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</Box>
|
||||
{draggedItemId && dragPos && (() => {
|
||||
const draggedItem = items.find(i => i.id === draggedItemId);
|
||||
if (!draggedItem) return null;
|
||||
|
||||
const texture = `https://cdn.minecraft.popa-popa.ru/textures/${draggedItem.item_data.material.toLowerCase()}.png`;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
left: dragPos.x,
|
||||
top: dragPos.y,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
//width: '12vw',
|
||||
//height: '12vw',
|
||||
pointerEvents: 'none',
|
||||
zIndex: 9999,
|
||||
opacity: 0.9,
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
...CardFacePaperSx,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '12vw',
|
||||
height: '12vw',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="img"
|
||||
src={texture}
|
||||
sx={{
|
||||
width: '5vw',
|
||||
height: '5vw',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
/>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
})()}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user