Compare commits

..

No commits in common. "12a315b04ab7b6e30f2721f711f31d22689cb8cf" and "2a287480b663f15af5b2ce509a1222451cacefd8" have entirely different histories.

6 changed files with 23 additions and 121 deletions

View File

@ -16,8 +16,7 @@ import { CreateProgramModal } from './Modals/CreateProgramProjectModal';
import { SelectExpenseItemModal } from './Modals/SelectExpenseItemModal';
import { SelectVspModal } from './Modals/SelectVspModal';
import { additionExpenseRowTable, additionVspRowTable } from './constants/addingRowConfig';
import { BASE_TABLE_CONFIG, TABLE_ROW_HEIGHT, getTableBodyCellProps, getTablePaperStyles, getTableRowHeightPx } from './constants/tableConfig';
import { createFixedRowVirtualizerOptions } from './utils/virtualizerOptions';
import { BASE_TABLE_CONFIG, TABLE_ROW_HEIGHT, getTableBodyCellProps, getTablePaperStyles } from './constants/tableConfig';
import { useRealtimeActions, useVspOptions } from './contexts/RealtimeContext';
import { useColumnSettings } from './hooks/useColumnSettings';
import { useHeaderPortal } from './hooks/useHeaderPortal';
@ -35,11 +34,13 @@ import useDenomination from '../common/useDenomination';
const CONFIG_CACHE = new Map();
const ROW_VIRTUALIZER_OPTIONS = createFixedRowVirtualizerOptions({
rowHeight: getTableRowHeightPx,
const ROW_VIRTUALIZER_OPTIONS = {
overscan: 5,
extra: { scrollPaddingStart: 0, scrollPaddingEnd: 0 },
});
scrollPaddingStart: 0,
scrollPaddingEnd: 0,
estimateSize: () => TABLE_ROW_HEIGHT,
measureElement: (element) => element?.offsetHeight || TABLE_ROW_HEIGHT,
};
const hasVspDropdown = (columns) =>
columns.some((column) => column.editType === 'vsp_dropdown' || (column.columns?.length && hasVspDropdown(column.columns)));
@ -361,7 +362,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
muiTableBodyRowProps: ({ row }) => ({
onClick: () => handleClickRowCell(row.id),
hover: false,
style: { height: TABLE_ROW_HEIGHT },
sx: {
'& > td::after': {
content: row.getIsSelected() ? '""' : 'none',

View File

@ -1,14 +1,9 @@
import { getRootFontSizePx } from '../utils/virtualizerOptions';
// Высота строки листа = 3rem (так было фактически: minHeight/maxHeight ячейки).
// rem зависит от ширины окна (App.css: .83vw при ширине <= 1920px), поэтому в px высота
// вычисляется в момент вызова. Виртуализатор строк не читает layout (measureElement = константа),
// значит стили строки/ячейки и эта высота обязаны совпадать.
export const TABLE_ROW_HEIGHT_REM = 3;
export const TABLE_ROW_HEIGHT = `${TABLE_ROW_HEIGHT_REM}rem`;
export const getTableRowHeightPx = () => TABLE_ROW_HEIGHT_REM * getRootFontSizePx();
export const TABLE_ROW_HEIGHT = 80;
export const BASE_TABLE_CONFIG = {
muiTableRowProps: {
sx: { height: '5rem' },
},
enableExpanding: true,
enableStickyHeader: true,
enableColumnPinning: true,
@ -45,8 +40,8 @@ export const getTableHeadCellStyles = () => ({
whiteSpace: 'auto',
wordBreak: 'normal',
boxSizing: 'border-box',
minHeight: TABLE_ROW_HEIGHT,
maxHeight: TABLE_ROW_HEIGHT,
minHeight: '3rem',
maxHeight: '3rem',
border: '1px solid #E6E6E6',
fontSize: '11px',
fontFamily: 'Calibri, Arial, sans-serif',
@ -61,8 +56,6 @@ export const getTableBodyCellProps = ({ column, table }) => {
sx: {
...getTableHeadCellStyles().sx,
fontSize: '1rem',
height: TABLE_ROW_HEIGHT,
overflow: 'hidden',
...(isPinned && { zIndex: 5 }),
},
};

View File

@ -1,27 +0,0 @@
/**
* Опции виртуализатора строк с фиксированной высотой.
*
* measureElement по умолчанию читает layout (getBoundingClientRect/offsetHeight) на каждой строке,
* что вызывает синхронный пересчёт раскладки. Здесь он возвращает константу, элемент не читается.
* Поэтому высоту строки нужно задавать явно в стилях (строке и ячейке) тем же значением.
*
* rowHeight: число (px) или функция, возвращающая px (когда высота задана в rem и зависит от ширины окна).
*/
export const createFixedRowVirtualizerOptions = ({ rowHeight, overscan, extra = {} }) => {
const getHeight = typeof rowHeight === 'function' ? rowHeight : () => rowHeight;
return {
...extra,
overscan,
estimateSize: () => getHeight(),
measureElement: () => getHeight(),
};
};
// Размер 1rem в px по правилу App.css: .83vw при ширине окна <= 1920px, иначе 16px.
// Считается по window.innerWidth, чтобы не читать layout и не вызывать пересчёт стилей.
const REM_BASE_PX = 16;
const REM_VW_RATIO = 0.0083;
const REM_MAX_VIEWPORT = 1920;
export const getRootFontSizePx = (viewportWidth = typeof window === 'undefined' ? undefined : window.innerWidth) =>
viewportWidth && viewportWidth <= REM_MAX_VIEWPORT ? viewportWidth * REM_VW_RATIO : REM_BASE_PX;

View File

@ -1,51 +0,0 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { createFixedRowVirtualizerOptions, getRootFontSizePx } from './virtualizerOptions.js';
const throwingElement = new Proxy(
{},
{
get(_target, prop) {
throw new Error(`Чтение свойства элемента запрещено: ${String(prop)}`);
},
},
);
test('measureElement не обращается к элементу и возвращает фиксированную высоту', () => {
const { measureElement } = createFixedRowVirtualizerOptions({ rowHeight: 40, overscan: 3 });
assert.equal(measureElement(throwingElement), 40);
assert.equal(measureElement(null), 40);
});
test('estimateSize возвращает высоту для любого индекса, overscan пробрасывается', () => {
const options = createFixedRowVirtualizerOptions({ rowHeight: 40, overscan: 3 });
assert.equal(options.estimateSize(0), 40);
assert.equal(options.estimateSize(999), 40);
assert.equal(options.overscan, 3);
});
test('rowHeight-функция вычисляется при каждом вызове (rem зависит от ширины окна)', () => {
let height = 40;
const options = createFixedRowVirtualizerOptions({ rowHeight: () => height, overscan: 5 });
assert.equal(options.estimateSize(0), 40);
height = 48;
assert.equal(options.estimateSize(0), 48);
assert.equal(options.measureElement(throwingElement), 48);
});
test('дополнительные опции добавляются и не перекрывают основные', () => {
const options = createFixedRowVirtualizerOptions({
rowHeight: 40,
overscan: 5,
extra: { scrollPaddingStart: 0, estimateSize: () => 1 },
});
assert.equal(options.scrollPaddingStart, 0);
assert.equal(options.estimateSize(0), 40);
});
test('getRootFontSizePx повторяет правило App.css', () => {
assert.equal(getRootFontSizePx(2560), 16);
assert.equal(getRootFontSizePx(1920), 1920 * 0.0083);
assert.ok(Math.abs(getRootFontSizePx(1600) * 3 - 39.84) < 0.01);
assert.equal(getRootFontSizePx(undefined), 16);
});

View File

@ -9,7 +9,6 @@ import { HeaderSwitch } from '../../components/HeaderMenu/HeaderSwitch';
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
import DenominationSelect from '../../components/common/DenominationSelect';
import useDenomination from '../../components/common/useDenomination';
import { createFixedRowVirtualizerOptions, getRootFontSizePx } from '../../components/RealtimeTable/utils/virtualizerOptions';
import { useSvod } from '../SvodPage/SvodContext';
import { SvodFilters } from '../SvodPage/SvodFilters';
@ -22,13 +21,12 @@ const createInitialTableState = () =>
const QUARTERS = [1, 2, 3, 4];
const tableSize = (rem) => rem * 16;
const numberFormatter = new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 });
// Высота строки задана в rem явно (строка и ячейка), виртуализатор layout не читает.
const ROW_HEIGHT_REM = 2.75;
const ROW_HEIGHT = `${ROW_HEIGHT_REM}rem`;
const ROW_VIRTUALIZER_OPTIONS = createFixedRowVirtualizerOptions({
rowHeight: () => ROW_HEIGHT_REM * getRootFontSizePx(),
overscan: 3,
});
const ROW_HEIGHT = 37;
const ROW_VIRTUALIZER_OPTIONS = {
overscan: 8,
estimateSize: () => ROW_HEIGHT,
measureElement: (element) => element?.offsetHeight || ROW_HEIGHT,
};
const COLUMN_BAND_COLORS = {
head: ['#f8f9fa', '#f1f7f3'],
body: ['#ffffff', '#f8fbf9'],
@ -534,12 +532,8 @@ export default function Svod2Page() {
},
}),
muiTableHeadRowProps: { sx: { boxShadow: 'none' } },
muiTableBodyRowProps: { style: { height: ROW_HEIGHT } },
muiTableBodyCellProps: ({ column }) => ({
sx: {
boxSizing: 'border-box',
height: ROW_HEIGHT,
overflow: 'hidden',
backgroundColor: COLUMN_BAND_COLORS.body[columnBands.get(column.id) ?? 0],
borderRight: columnGroupEnds.has(column.id) ? '0.1875rem solid #9eafa3' : '0.0625rem solid #edf0f2',
fontSize: '0.8125rem',

View File

@ -5,16 +5,13 @@ import useDenomination from '../../components/common/useDenomination';
import { blueColumn, greenColumn, orangeColumn } from '../../components/RealtimeTable/constants/columnColors';
import { sectionCodeColor } from '../../components/RealtimeTable/constants/columnConfig';
import { buildRowHierarchy } from '../../components/RealtimeTable/utils/rowUtils';
import { createFixedRowVirtualizerOptions, getRootFontSizePx } from '../../components/RealtimeTable/utils/virtualizerOptions';
const tableSize = (rem) => rem * 16;
// Высота строки задана в rem явно (строка и ячейка), виртуализатор layout не читает.
const ROW_HEIGHT_REM = 2.25;
const ROW_HEIGHT = `${ROW_HEIGHT_REM}rem`;
const ROW_VIRTUALIZER_OPTIONS = createFixedRowVirtualizerOptions({
rowHeight: () => ROW_HEIGHT_REM * getRootFontSizePx(),
const ROW_VIRTUALIZER_OPTIONS = {
overscan: 8,
});
estimateSize: () => 37,
measureElement: (element) => element?.offsetHeight || 37,
};
const SOURCE_COLORS = { ahr: greenColumn, oper: blueColumn, cap: orangeColumn };
@ -160,7 +157,6 @@ export const SummaryTable = memo(function SummaryTable({ columns, data, loading,
boxShadow: 'none',
},
},
muiTableBodyRowProps: { style: { height: ROW_HEIGHT } },
muiTableBodyCellProps: ({ row, column }) => {
const backgroundColor = getSummaryRowColor(row, colorBySource);
return {
@ -171,9 +167,6 @@ export const SummaryTable = memo(function SummaryTable({ columns, data, loading,
fontSize: '0.8125rem',
px: column.id === 'mrt-row-expand' ? '0.25rem' : '0.75rem',
py: column.id === 'mrt-row-expand' ? 0 : '0.5rem',
boxSizing: 'border-box',
height: ROW_HEIGHT,
overflow: 'hidden',
whiteSpace: 'nowrap',
textAlign: ['section_code', 'name'].includes(column.id) ? 'left' : 'right',
},