add component CoinsDisplay and rework CustomTooltip
This commit is contained in:
233
src/renderer/components/CoinsDisplay.tsx
Normal file
233
src/renderer/components/CoinsDisplay.tsx
Normal file
@ -0,0 +1,233 @@
|
||||
// CoinsDisplay.tsx
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import CustomTooltip from './CustomTooltip';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { fetchCoins } from '../api';
|
||||
|
||||
interface CoinsDisplayProps {
|
||||
// Основные пропсы
|
||||
value?: number; // Передаем значение напрямую
|
||||
username?: string; // Или получаем по username из API
|
||||
|
||||
// Опции отображения
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
showTooltip?: boolean;
|
||||
tooltipText?: string;
|
||||
showIcon?: boolean;
|
||||
iconColor?: string;
|
||||
|
||||
// Опции обновления
|
||||
autoUpdate?: boolean; // Автоматическое обновление из API
|
||||
updateInterval?: number; // Интервал обновления в миллисекундах
|
||||
|
||||
// Стилизация
|
||||
backgroundColor?: string;
|
||||
textColor?: string;
|
||||
}
|
||||
|
||||
export default function CoinsDisplay({
|
||||
// Основные пропсы
|
||||
value: externalValue,
|
||||
username,
|
||||
|
||||
// Опции отображения
|
||||
size = 'medium',
|
||||
showTooltip = true,
|
||||
tooltipText = 'Попы — внутриигровая валюта, начисляемая за время игры на серверах.',
|
||||
showIcon = true,
|
||||
iconColor = '#2bff00ff',
|
||||
|
||||
// Опции обновления
|
||||
autoUpdate = false,
|
||||
updateInterval = 60000,
|
||||
|
||||
// Стилизация
|
||||
backgroundColor = 'rgba(0, 0, 0, 0.2)',
|
||||
textColor = 'white',
|
||||
}: CoinsDisplayProps) {
|
||||
const [coins, setCoins] = useState<number>(externalValue || 0);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
// Определяем размеры в зависимости от параметра size
|
||||
const getSizes = () => {
|
||||
switch (size) {
|
||||
case 'small':
|
||||
return {
|
||||
containerPadding: '4px 8px',
|
||||
iconSize: '16px',
|
||||
fontSize: '12px',
|
||||
borderRadius: '12px',
|
||||
gap: '6px',
|
||||
};
|
||||
case 'large':
|
||||
return {
|
||||
containerPadding: '8px 16px',
|
||||
iconSize: '28px',
|
||||
fontSize: '18px',
|
||||
borderRadius: '20px',
|
||||
gap: '10px',
|
||||
};
|
||||
case 'medium':
|
||||
default:
|
||||
return {
|
||||
containerPadding: '6px 12px',
|
||||
iconSize: '24px',
|
||||
fontSize: '16px',
|
||||
borderRadius: '16px',
|
||||
gap: '8px',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const sizes = getSizes();
|
||||
|
||||
// Функция для получения количества монет из API
|
||||
const fetchCoinsData = async () => {
|
||||
if (!username) return;
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const coinsData = await fetchCoins(username);
|
||||
setCoins(coinsData.coins);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении количества монет:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Эффект для внешнего значения
|
||||
useEffect(() => {
|
||||
if (externalValue !== undefined) {
|
||||
setCoins(externalValue);
|
||||
}
|
||||
}, [externalValue]);
|
||||
|
||||
// Эффект для API обновлений
|
||||
useEffect(() => {
|
||||
if (username && autoUpdate) {
|
||||
fetchCoinsData();
|
||||
|
||||
// Создаем интервалы для периодического обновления данных
|
||||
const coinsInterval = setInterval(fetchCoinsData, updateInterval);
|
||||
|
||||
return () => {
|
||||
clearInterval(coinsInterval);
|
||||
};
|
||||
}
|
||||
}, [username, autoUpdate, updateInterval]);
|
||||
|
||||
// Ручное обновление данных
|
||||
const handleRefresh = () => {
|
||||
if (username) {
|
||||
fetchCoinsData();
|
||||
}
|
||||
};
|
||||
|
||||
// Форматирование числа с разделителями тысяч
|
||||
const formatNumber = (num: number): string => {
|
||||
return num.toLocaleString('ru-RU');
|
||||
};
|
||||
|
||||
const coinsDisplay = (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: sizes.gap,
|
||||
backgroundColor,
|
||||
borderRadius: sizes.borderRadius,
|
||||
padding: sizes.containerPadding,
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
cursor: showTooltip ? 'help' : 'default',
|
||||
opacity: isLoading ? 0.7 : 1,
|
||||
transition: 'opacity 0.2s ease',
|
||||
}}
|
||||
onClick={username ? handleRefresh : undefined}
|
||||
title={username ? 'Нажмите для обновления' : undefined}
|
||||
>
|
||||
{showIcon && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: sizes.iconSize,
|
||||
height: sizes.iconSize,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: iconColor,
|
||||
fontWeight: 'bold',
|
||||
fontSize: `calc(${sizes.fontSize} * 0.8)`,
|
||||
}}
|
||||
>
|
||||
P
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
color: textColor,
|
||||
fontWeight: 'bold',
|
||||
fontSize: sizes.fontSize,
|
||||
lineHeight: 1,
|
||||
fontFamily: 'Benzin-Bold, sans-serif',
|
||||
}}
|
||||
>
|
||||
{isLoading ? '...' : formatNumber(coins)}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
if (showTooltip) {
|
||||
return (
|
||||
<CustomTooltip
|
||||
title={tooltipText}
|
||||
arrow
|
||||
placement="bottom"
|
||||
TransitionProps={{ timeout: 300 }}
|
||||
>
|
||||
{coinsDisplay}
|
||||
</CustomTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return coinsDisplay;
|
||||
}
|
||||
|
||||
// Примеры использования в комментариях для разработчика:
|
||||
/*
|
||||
// Пример 1: Простое отображение числа
|
||||
<CoinsDisplay value={1500} />
|
||||
|
||||
// Пример 2: Получение данных по username с автообновлением
|
||||
<CoinsDisplay
|
||||
username="player123"
|
||||
autoUpdate={true}
|
||||
updateInterval={30000} // обновлять каждые 30 секунд
|
||||
/>
|
||||
|
||||
// Пример 3: Кастомная стилизация без иконки
|
||||
<CoinsDisplay
|
||||
value={9999}
|
||||
size="small"
|
||||
showIcon={false}
|
||||
showTooltip={false}
|
||||
backgroundColor="rgba(255, 100, 100, 0.2)"
|
||||
textColor="#ffcc00"
|
||||
/>
|
||||
|
||||
// Пример 4: Большой отображение для профиля
|
||||
<CoinsDisplay
|
||||
username="player123"
|
||||
size="large"
|
||||
tooltipText="Ваш текущий баланс"
|
||||
iconColor="#00ffaa"
|
||||
/>
|
||||
*/
|
||||
@ -8,16 +8,28 @@ const CustomTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
))(({ theme }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 1)',
|
||||
color: 'white',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
||||
color: '#fff',
|
||||
maxWidth: 300,
|
||||
fontSize: '0.9vw',
|
||||
border: '1px solid rgba(255, 77, 77, 0.5)',
|
||||
border: '1px solid rgba(242, 113, 33, 0.5)',
|
||||
borderRadius: '1vw',
|
||||
padding: '1vw',
|
||||
boxShadow:
|
||||
'0 0 1vw rgba(255, 77, 77, 0.3), inset 0.8vw -0.8vw 2vw rgba(255, 77, 77, 0.15)',
|
||||
boxShadow: `
|
||||
0 0 1.5vw rgba(242, 113, 33, 0.4),
|
||||
0 0 0.5vw rgba(233, 64, 87, 0.3),
|
||||
inset 0 0 0.5vw rgba(138, 35, 135, 0.2)
|
||||
`,
|
||||
fontFamily: 'Benzin-Bold',
|
||||
background: `
|
||||
linear-gradient(
|
||||
135deg,
|
||||
rgba(0, 0, 0, 0.95) 0%,
|
||||
rgba(20, 20, 20, 0.95) 100%
|
||||
)
|
||||
`,
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
@ -26,12 +38,37 @@ const CustomTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
borderRadius: '1vw',
|
||||
// background: 'linear-gradient(45deg, rgba(255, 77, 77, 0.1), transparent)',
|
||||
padding: '2px',
|
||||
background: `
|
||||
linear-gradient(
|
||||
135deg,
|
||||
rgba(242, 113, 33, 0.8) 0%,
|
||||
rgba(233, 64, 87, 0.6) 50%,
|
||||
rgba(138, 35, 135, 0.4) 100%
|
||||
)
|
||||
`,
|
||||
WebkitMask: `
|
||||
linear-gradient(#fff 0 0) content-box,
|
||||
linear-gradient(#fff 0 0)
|
||||
`,
|
||||
WebkitMaskComposite: 'xor',
|
||||
maskComposite: 'exclude',
|
||||
zIndex: -1,
|
||||
},
|
||||
},
|
||||
[`& .${tooltipClasses.arrow}`]: {
|
||||
color: 'rgba(255, 77, 77, 0.5)',
|
||||
color: 'rgba(242, 113, 33, 0.9)',
|
||||
'&::before': {
|
||||
background: `
|
||||
linear-gradient(
|
||||
135deg,
|
||||
rgba(242, 113, 33, 0.9) 0%,
|
||||
rgba(233, 64, 87, 0.7) 50%,
|
||||
rgba(138, 35, 135, 0.5) 100%
|
||||
)
|
||||
`,
|
||||
border: '1px solid rgba(242, 113, 33, 0.5)',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { fetchCoins } from '../api';
|
||||
import CustomTooltip from './CustomTooltip';
|
||||
import CoinsDisplay from './CoinsDisplay';
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: {
|
||||
@ -208,111 +209,118 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tabs
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
aria-label="basic tabs example"
|
||||
variant="scrollable"
|
||||
scrollButtons={false}
|
||||
disableRipple={true}
|
||||
sx={{ maxWidth: '42vw' }}
|
||||
<CustomTooltip
|
||||
title={'Покрути колесиком мыши чтобы увидеть больше кнопок'}
|
||||
arrow
|
||||
placement="bottom"
|
||||
TransitionProps={{ timeout: 100 }}
|
||||
>
|
||||
<Tab
|
||||
label="Новости"
|
||||
<Tabs
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
aria-label="basic tabs example"
|
||||
variant="scrollable"
|
||||
scrollButtons={false}
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('news');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Версии"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('versions');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Профиль"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('profile');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Магазин"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('shop');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Рынок"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('marketplace');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
sx={{ maxWidth: '42vw' }}
|
||||
>
|
||||
<Tab
|
||||
label="Новости"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('news');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Версии"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('versions');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Профиль"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('profile');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Магазин"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('shop');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Рынок"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('marketplace');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</CustomTooltip>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@ -376,46 +384,12 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
)}
|
||||
{/* Кнопка регистрации, если на странице логина */}
|
||||
{!isLoginPage && !isRegistrationPage && username && (
|
||||
<CustomTooltip
|
||||
title="Попы — внутриигровая валюта, начисляемая за время игры на серверах."
|
||||
arrow
|
||||
placement="bottom"
|
||||
TransitionProps={{ timeout: 300 }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||
borderRadius: '16px',
|
||||
padding: '6px 12px',
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: '#2bff00ff' }}>P</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
fontSize: '16px',
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
{coins}
|
||||
</Typography>
|
||||
</Box>
|
||||
</CustomTooltip>
|
||||
<CoinsDisplay
|
||||
username={username}
|
||||
size="medium"
|
||||
autoUpdate={true}
|
||||
showTooltip={true}
|
||||
/>
|
||||
)}
|
||||
{isLoginPage && (
|
||||
<Button
|
||||
|
||||
@ -29,6 +29,7 @@ import { useEffect, useState } from 'react';
|
||||
import { FullScreenLoader } from '../components/FullScreenLoader';
|
||||
import { getPlayerServer } from '../utils/playerOnlineCheck';
|
||||
import CaseRoulette from '../components/CaseRoulette';
|
||||
import CoinsDisplay from '../components/CoinsDisplay';
|
||||
|
||||
function getRarityByWeight(
|
||||
weight?: number,
|
||||
@ -91,8 +92,6 @@ export default function Shop() {
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
const ITEM_WIDTH = 110; // ширина "карточки" предмета в рулетке
|
||||
const ITEM_GAP = 8;
|
||||
const VISIBLE_ITEMS = 21; // сколько элементов в линии
|
||||
const CENTER_INDEX = Math.floor(VISIBLE_ITEMS / 2);
|
||||
|
||||
@ -327,11 +326,7 @@ export default function Shop() {
|
||||
</Typography>
|
||||
|
||||
{!isOnline && (
|
||||
<Typography
|
||||
variant="body1"
|
||||
color="error"
|
||||
sx={{ mb: 2, maxWidth: '600px' }}
|
||||
>
|
||||
<Typography variant="body1" color="error" sx={{ mb: 2 }}>
|
||||
Для открытия кейсов вам необходимо находиться на одном из серверов
|
||||
игры. Зайдите в игру и обновите страницу.
|
||||
</Typography>
|
||||
@ -475,20 +470,12 @@ export default function Shop() {
|
||||
>
|
||||
Цена
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
px: 1.6,
|
||||
py: 0.4,
|
||||
borderRadius: '999px',
|
||||
fontSize: '0.8rem',
|
||||
bgcolor: 'rgba(255, 77, 77, 0.16)',
|
||||
border: '1px solid rgba(255, 77, 77, 0.85)',
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
}}
|
||||
>
|
||||
{c.price} поп
|
||||
</Box>
|
||||
<CoinsDisplay
|
||||
value={c.price}
|
||||
size="small"
|
||||
autoUpdate={false}
|
||||
showTooltip={true}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{typeof c.items_count === 'number' && (
|
||||
|
||||
Reference in New Issue
Block a user