144 lines
3.9 KiB
JavaScript
144 lines
3.9 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import { useMemo } from 'react';
|
|
import styled from '@emotion/styled';
|
|
import { useRealtime } from '../../contexts/RealtimeContext';
|
|
import { toast } from 'react-toastify';
|
|
|
|
const ContainerForText = styled.span`
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 3;
|
|
overflow: hidden;
|
|
white-space: pre-wrap;
|
|
`;
|
|
|
|
// Стилизованный компонент для плашки блокировки
|
|
const LockBadge = styled.div`
|
|
position: absolute;
|
|
top: 2px;
|
|
right: 2px;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
color: white;
|
|
font-size: 9px;
|
|
padding: 1px 4px;
|
|
border-radius: 3px;
|
|
pointer-events: none;
|
|
z-index: 10;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
letter-spacing: 0.3px;
|
|
backdrop-filter: blur(4px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
`;
|
|
|
|
// Функция для подсветки текста
|
|
const highlightText = (text, searchQuery) => {
|
|
if (!searchQuery || !text) return text;
|
|
|
|
const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
const regex = new RegExp(`(${escapedQuery})`, 'gi');
|
|
|
|
const parts = text.split(regex);
|
|
|
|
return parts.map((part, index) =>
|
|
regex.test(part) ? (
|
|
<mark
|
|
key={index}
|
|
style={{
|
|
backgroundColor: '#ffeb3b',
|
|
color: '#000',
|
|
fontWeight: 'bold',
|
|
padding: '0 2px',
|
|
borderRadius: '2px',
|
|
}}
|
|
>
|
|
{part}
|
|
</mark>
|
|
) : (
|
|
part
|
|
),
|
|
);
|
|
};
|
|
|
|
// Функция для форматирования числа в финансовый формат
|
|
const formatNumber = (value) => {
|
|
if (value === null || value === undefined || value === '') return String(value || '');
|
|
|
|
const num = Number(value);
|
|
if (isNaN(num)) return String(value);
|
|
|
|
return num.toLocaleString('ru-RU', {
|
|
minimumFractionDigits: 1,
|
|
maximumFractionDigits: 1,
|
|
useGrouping: true,
|
|
});
|
|
};
|
|
|
|
const Cell = React.memo(({ cell, row, column, onClick, globalFilter, backgroundColor, color, isEditable }) => {
|
|
const { lockedCells } = useRealtime();
|
|
const cellKey = `${row.id}_${column.id}`;
|
|
|
|
const isLocked = lockedCells.includes(cellKey);
|
|
|
|
const cellValue = cell.getValue();
|
|
let textValue = String(cellValue || '');
|
|
|
|
const isNumeric = !isNaN(Number(cellValue)) && cellValue !== null && cellValue !== undefined && cellValue !== '';
|
|
if (isNumeric) {
|
|
textValue = formatNumber(cellValue);
|
|
}
|
|
|
|
const highlightedContent = useMemo(() => {
|
|
return highlightText(textValue, globalFilter);
|
|
}, [textValue, globalFilter]);
|
|
|
|
// Базовые стили для заблокированной ячейки
|
|
const lockedStyles = isLocked ? {
|
|
border: '2px solid #e0e0e0',
|
|
backgroundColor: '#f5f5f5',
|
|
color: '#999999',
|
|
cursor: 'not-allowed',
|
|
opacity: 0.85,
|
|
} : {};
|
|
|
|
// Если isEditable false, добавляем дополнительные стили
|
|
const editableStyles = !isEditable ? {
|
|
border: '2px solid #d3d3d3',
|
|
color: backgroundColor?.toLowerCase() === '#933634' ? '#ffffff' : '#525252',
|
|
cursor: 'not-allowed',
|
|
} : {};
|
|
|
|
return (
|
|
<div
|
|
className="cell"
|
|
style={{
|
|
width: '100%',
|
|
height: '100%',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
boxSizing: 'border-box',
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
border: '2px solid transparent',
|
|
padding: '8px',
|
|
backgroundColor: isLocked ? '#f5f5f5' : backgroundColor,
|
|
color: isLocked ? '#999999' : color,
|
|
...lockedStyles,
|
|
...editableStyles,
|
|
}}
|
|
onClick={isLocked ? undefined : onClick}
|
|
>
|
|
<ContainerForText>{highlightedContent}</ContainerForText>
|
|
{isLocked && (
|
|
<LockBadge>
|
|
🔒
|
|
</LockBadge>
|
|
)}
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export default Cell; |