@astral/ui 4.114.0 → 4.115.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/components/DataGrid/Body/Body.d.ts +1 -1
  2. package/components/DataGrid/Body/Body.js +7 -3
  3. package/components/DataGrid/Body/types.d.ts +11 -3
  4. package/components/DataGrid/DataGrid.js +0 -1
  5. package/components/DataGrid/PinnedSections/PinnedSections.js +2 -2
  6. package/components/DataGrid/PinnedSections/useLogic/useLogic.d.ts +1 -0
  7. package/components/DataGrid/PinnedSections/useLogic/useLogic.js +4 -3
  8. package/components/DataGrid/Row/Row.js +13 -9
  9. package/components/DataGrid/Row/RowContext/RowContext.d.ts +15 -4
  10. package/components/DataGrid/Row/RowContext/RowContext.js +3 -3
  11. package/components/DataGrid/Row/RowContext/RowProvider/RowProvider.d.ts +10 -1
  12. package/components/DataGrid/Row/RowContext/RowProvider/RowProvider.js +23 -17
  13. package/components/DataGrid/Row/types.d.ts +7 -3
  14. package/components/DataGrid/Row/useLogic/useLogic.d.ts +6 -4
  15. package/components/DataGrid/Row/useLogic/useLogic.js +21 -12
  16. package/components/DataGrid/RowBase/RowBase.js +5 -2
  17. package/components/DataGrid/RowBase/types.d.ts +9 -5
  18. package/components/DataGrid/RowBase/useLogic/useLogic.d.ts +5 -4
  19. package/components/DataGrid/RowBase/useLogic/useLogic.js +16 -10
  20. package/components/DataGrid/RowCheckbox/RowCheckbox.d.ts +5 -0
  21. package/components/DataGrid/RowCheckbox/RowCheckbox.js +13 -0
  22. package/components/DataGrid/RowCheckbox/constants.d.ts +4 -0
  23. package/components/DataGrid/RowCheckbox/constants.js +4 -0
  24. package/components/DataGrid/RowCheckbox/index.d.ts +1 -0
  25. package/components/DataGrid/RowCheckbox/index.js +1 -0
  26. package/components/DataGrid/RowCheckbox/types.d.ts +7 -0
  27. package/components/DataGrid/RowCheckbox/types.js +1 -0
  28. package/components/DataGrid/hooks/index.d.ts +1 -0
  29. package/components/DataGrid/hooks/index.js +1 -0
  30. package/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/index.d.ts +1 -0
  31. package/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/index.js +1 -0
  32. package/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/useWarnRowsWithoutDisabledReason.d.ts +5 -0
  33. package/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/useWarnRowsWithoutDisabledReason.js +17 -0
  34. package/components/DataGrid/index.d.ts +1 -1
  35. package/components/DataGrid/public.d.ts +1 -1
  36. package/components/DataGrid/tests/performance/testDataGridPerformance.d.ts +3 -1
  37. package/components/DataGrid/tests/performance/testDataGridPerformance.js +234 -2
  38. package/components/DataGrid/tests/selection/testDataGridSelection.d.ts +14 -0
  39. package/components/DataGrid/tests/selection/testDataGridSelection.js +562 -0
  40. package/components/DataGrid/types.d.ts +34 -1
  41. package/components/DataGrid/useLogic/useLogic.d.ts +6 -3
  42. package/components/DataGrid/useLogic/useLogic.js +15 -10
  43. package/components/DataGrid/utils/getRowDisabledState/getRowDisabledState.d.ts +31 -0
  44. package/components/DataGrid/utils/getRowDisabledState/getRowDisabledState.js +20 -0
  45. package/components/DataGrid/utils/getRowDisabledState/index.d.ts +1 -0
  46. package/components/DataGrid/utils/getRowDisabledState/index.js +1 -0
  47. package/components/DataGrid/utils/index.d.ts +3 -0
  48. package/components/DataGrid/utils/index.js +3 -0
  49. package/components/DataGrid/utils/resolveRowDisabled/index.d.ts +1 -0
  50. package/components/DataGrid/utils/resolveRowDisabled/index.js +1 -0
  51. package/components/DataGrid/utils/resolveRowDisabled/resolveRowDisabled.d.ts +5 -0
  52. package/components/DataGrid/utils/resolveRowDisabled/resolveRowDisabled.js +17 -0
  53. package/components/DataGrid/utils/resolveRowsDisabled/index.d.ts +1 -0
  54. package/components/DataGrid/utils/resolveRowsDisabled/index.js +1 -0
  55. package/components/DataGrid/utils/resolveRowsDisabled/resolveRowsDisabled.d.ts +16 -0
  56. package/components/DataGrid/utils/resolveRowsDisabled/resolveRowsDisabled.js +17 -0
  57. package/components/DataGridActionCell/hooks/useBlockingActions/useBlockingActions.js +3 -3
  58. package/components/DataGridCollapsibleRow/useLogic/useLogic.d.ts +3 -2
  59. package/components/DataGridCollapsibleRow/useLogic/useLogic.js +4 -3
  60. package/components/DataGridInfinite/DataGridInfinite.js +4 -4
  61. package/components/DataGridInfinite/DataGridInfiniteContext/DataGridInfiniteContext.d.ts +1 -1
  62. package/components/DataGridInfinite/DataGridInfiniteContext/DataGridInfiniteContext.js +1 -1
  63. package/components/DataGridInfinite/Footer/Footer.js +2 -2
  64. package/components/DataGridInfinite/Row/Row.js +2 -2
  65. package/components/DataGridInfinite/Row/index.d.ts +1 -0
  66. package/components/DataGridInfinite/Row/types.d.ts +17 -2
  67. package/components/DataGridInfinite/Row/useLogic/useLogic.d.ts +4 -2
  68. package/components/DataGridInfinite/Row/useLogic/useLogic.js +12 -4
  69. package/components/DataGridInfinite/constants.d.ts +2 -0
  70. package/components/DataGridInfinite/constants.js +2 -0
  71. package/components/DataGridInfinite/faker.d.ts +1 -5
  72. package/components/DataGridInfinite/faker.js +1 -2
  73. package/components/DataGridInfinite/useLogic/useLogic.d.ts +3 -2
  74. package/components/DataGridInfinite/useLogic/useLogic.js +25 -13
  75. package/components/PageContent/styles.js +25 -1
  76. package/components/index.d.ts +1 -1
  77. package/node/components/DataGrid/Body/Body.d.ts +1 -1
  78. package/node/components/DataGrid/Body/Body.js +7 -3
  79. package/node/components/DataGrid/Body/types.d.ts +11 -3
  80. package/node/components/DataGrid/DataGrid.js +0 -1
  81. package/node/components/DataGrid/PinnedSections/PinnedSections.js +2 -2
  82. package/node/components/DataGrid/PinnedSections/useLogic/useLogic.d.ts +1 -0
  83. package/node/components/DataGrid/PinnedSections/useLogic/useLogic.js +4 -3
  84. package/node/components/DataGrid/Row/Row.js +13 -9
  85. package/node/components/DataGrid/Row/RowContext/RowContext.d.ts +15 -4
  86. package/node/components/DataGrid/Row/RowContext/RowContext.js +3 -3
  87. package/node/components/DataGrid/Row/RowContext/RowProvider/RowProvider.d.ts +10 -1
  88. package/node/components/DataGrid/Row/RowContext/RowProvider/RowProvider.js +23 -17
  89. package/node/components/DataGrid/Row/types.d.ts +7 -3
  90. package/node/components/DataGrid/Row/useLogic/useLogic.d.ts +6 -4
  91. package/node/components/DataGrid/Row/useLogic/useLogic.js +23 -14
  92. package/node/components/DataGrid/RowBase/RowBase.js +5 -2
  93. package/node/components/DataGrid/RowBase/types.d.ts +9 -5
  94. package/node/components/DataGrid/RowBase/useLogic/useLogic.d.ts +5 -4
  95. package/node/components/DataGrid/RowBase/useLogic/useLogic.js +16 -10
  96. package/node/components/DataGrid/RowCheckbox/RowCheckbox.d.ts +5 -0
  97. package/node/components/DataGrid/RowCheckbox/RowCheckbox.js +17 -0
  98. package/node/components/DataGrid/RowCheckbox/constants.d.ts +4 -0
  99. package/node/components/DataGrid/RowCheckbox/constants.js +7 -0
  100. package/node/components/DataGrid/RowCheckbox/index.d.ts +1 -0
  101. package/node/components/DataGrid/RowCheckbox/index.js +17 -0
  102. package/node/components/DataGrid/RowCheckbox/types.d.ts +7 -0
  103. package/node/components/DataGrid/RowCheckbox/types.js +2 -0
  104. package/node/components/DataGrid/hooks/index.d.ts +1 -0
  105. package/node/components/DataGrid/hooks/index.js +1 -0
  106. package/node/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/index.d.ts +1 -0
  107. package/node/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/index.js +17 -0
  108. package/node/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/useWarnRowsWithoutDisabledReason.d.ts +5 -0
  109. package/node/components/DataGrid/hooks/useWarnRowsWithoutDisabledReason/useWarnRowsWithoutDisabledReason.js +21 -0
  110. package/node/components/DataGrid/index.d.ts +1 -1
  111. package/node/components/DataGrid/public.d.ts +1 -1
  112. package/node/components/DataGrid/tests/performance/testDataGridPerformance.d.ts +3 -1
  113. package/node/components/DataGrid/tests/performance/testDataGridPerformance.js +232 -0
  114. package/node/components/DataGrid/tests/selection/testDataGridSelection.d.ts +14 -0
  115. package/node/components/DataGrid/tests/selection/testDataGridSelection.js +566 -0
  116. package/node/components/DataGrid/types.d.ts +34 -1
  117. package/node/components/DataGrid/useLogic/useLogic.d.ts +6 -3
  118. package/node/components/DataGrid/useLogic/useLogic.js +17 -12
  119. package/node/components/DataGrid/utils/getRowDisabledState/getRowDisabledState.d.ts +31 -0
  120. package/node/components/DataGrid/utils/getRowDisabledState/getRowDisabledState.js +24 -0
  121. package/node/components/DataGrid/utils/getRowDisabledState/index.d.ts +1 -0
  122. package/node/components/DataGrid/utils/getRowDisabledState/index.js +5 -0
  123. package/node/components/DataGrid/utils/index.d.ts +3 -0
  124. package/node/components/DataGrid/utils/index.js +7 -1
  125. package/node/components/DataGrid/utils/resolveRowDisabled/index.d.ts +1 -0
  126. package/node/components/DataGrid/utils/resolveRowDisabled/index.js +5 -0
  127. package/node/components/DataGrid/utils/resolveRowDisabled/resolveRowDisabled.d.ts +5 -0
  128. package/node/components/DataGrid/utils/resolveRowDisabled/resolveRowDisabled.js +21 -0
  129. package/node/components/DataGrid/utils/resolveRowsDisabled/index.d.ts +1 -0
  130. package/node/components/DataGrid/utils/resolveRowsDisabled/index.js +5 -0
  131. package/node/components/DataGrid/utils/resolveRowsDisabled/resolveRowsDisabled.d.ts +16 -0
  132. package/node/components/DataGrid/utils/resolveRowsDisabled/resolveRowsDisabled.js +21 -0
  133. package/node/components/DataGridActionCell/hooks/useBlockingActions/useBlockingActions.js +3 -3
  134. package/node/components/DataGridCollapsibleRow/useLogic/useLogic.d.ts +3 -2
  135. package/node/components/DataGridCollapsibleRow/useLogic/useLogic.js +4 -3
  136. package/node/components/DataGridInfinite/DataGridInfinite.js +4 -4
  137. package/node/components/DataGridInfinite/DataGridInfiniteContext/DataGridInfiniteContext.d.ts +1 -1
  138. package/node/components/DataGridInfinite/DataGridInfiniteContext/DataGridInfiniteContext.js +1 -1
  139. package/node/components/DataGridInfinite/Footer/Footer.js +1 -1
  140. package/node/components/DataGridInfinite/Row/Row.js +2 -2
  141. package/node/components/DataGridInfinite/Row/index.d.ts +1 -0
  142. package/node/components/DataGridInfinite/Row/types.d.ts +17 -2
  143. package/node/components/DataGridInfinite/Row/useLogic/useLogic.d.ts +4 -2
  144. package/node/components/DataGridInfinite/Row/useLogic/useLogic.js +12 -4
  145. package/node/components/DataGridInfinite/constants.d.ts +2 -0
  146. package/node/components/DataGridInfinite/constants.js +2 -0
  147. package/node/components/DataGridInfinite/faker.d.ts +1 -5
  148. package/node/components/DataGridInfinite/faker.js +1 -2
  149. package/node/components/DataGridInfinite/useLogic/useLogic.d.ts +3 -2
  150. package/node/components/DataGridInfinite/useLogic/useLogic.js +26 -14
  151. package/node/components/PageContent/styles.js +24 -0
  152. package/node/components/index.d.ts +1 -1
  153. package/package.json +1 -1
@@ -1,14 +1,14 @@
1
1
  import { useCallback, useContext, useEffect, useMemo, useRef, } from 'react';
2
2
  import { ConfigContext } from '../../ConfigProvider';
3
3
  import { useFirstMountState } from '../../useFirstMountState';
4
- import { prop } from '../../utils/prop';
5
4
  import { uniqueBy } from '../../utils/uniqueBy';
6
5
  import { dataGridClassnames, EXPANDED_LEVEL_BY_DEFAULT, INITIAL_OPENED_NESTED_CHILDREN_COUNT_ON_MOBILE_BY_DEFAULT, } from '../constants';
7
6
  import { Variant } from '../enums';
8
- import { checkIsMassActionsOpen, getDataGridCssVars, getFlattenColumns, getGridTemplateColumns, isGroupColumns, } from '../utils';
7
+ import { useWarnRowsWithoutDisabledReason } from '../hooks';
8
+ import { checkIsMassActionsOpen, getDataGridCssVars, getFlattenColumns, getGridTemplateColumns, isGroupColumns, resolveRowsDisabled, } from '../utils';
9
9
  import { useDataGridMobileLogic, usePinnedColumns } from './hooks';
10
10
  import { getIsAllowHorizontalScroll } from './utils';
11
- export const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, selectedRows = [], isLoading, isDisabled, mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', onSelectRow, isError, onRetry, errorMsg, isHideSelectAll, isHideHeaderIfNoData = false, sorting, className, onSort, maxHeight, gridViewMode: gridViewModeProp = 'standard', massActions, }) => {
11
+ export const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, selectedRows = [], isLoading, isDisabled, getRowDisabled, mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', onSelectRow, isError, onRetry, errorMsg, isHideSelectAll, isHideHeaderIfNoData = false, sorting, className, onSort, maxHeight, gridViewMode: gridViewModeProp = 'standard', massActions, }) => {
12
12
  const isFirstRender = useFirstMountState();
13
13
  const isSelectable = Boolean(onSelectRow);
14
14
  const isDataGridDisabled = isLoading || isDisabled;
@@ -57,22 +57,24 @@ export const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, se
57
57
  }
58
58
  return config;
59
59
  }, [variant, subrows, tree, isMobile]);
60
- const availableRows = rows.filter((row) => !(row.options?.isDisabled || row.options?.isNotSelectable));
60
+ // Блокировки строк: getRowDisabled вызывается один раз на строку
61
+ const { rowsDisabled, idsWithoutReason } = useMemo(() => resolveRowsDisabled(renderRows, keyId, getRowDisabled), [renderRows, keyId, getRowDisabled]);
62
+ useWarnRowsWithoutDisabledReason(idsWithoutReason);
63
+ const selectedIds = useMemo(() => new Set(selectedRows.map((row) => row[keyId])), [selectedRows, keyId]);
64
+ const availableRows = useMemo(() => renderRows.filter((row) => !row.options?.isNotSelectable && !rowsDisabled.get(row[keyId])), [renderRows, rowsDisabled, keyId]);
61
65
  const gridColumns = useMemo(() => getGridTemplateColumns(getFlattenColumns(columns)), [columns]);
62
66
  const isAllowHorizontalScroll = getIsAllowHorizontalScroll(getFlattenColumns(columns));
63
- const uncheckedRowsCount = useMemo(() => {
64
- return availableRows.filter((row) => !selectedRows.find((selectedRow) => selectedRow[keyId] === row[keyId])).length;
65
- }, [availableRows, selectedRows, keyId]);
67
+ const uncheckedRowsCount = useMemo(() => availableRows.filter((row) => !selectedIds.has(row[keyId])).length, [availableRows, selectedIds, keyId]);
66
68
  const handleSelectAllRows = (event) => {
67
69
  if (!onSelectRow) {
68
70
  return;
69
71
  }
70
72
  if (event.target.checked) {
71
- const mergedSelectedRows = uniqueBy([...selectedRows, ...availableRows], prop(keyId));
73
+ const mergedSelectedRows = uniqueBy([...selectedRowsRef.current, ...availableRows], (row) => row[keyId]);
72
74
  return onSelectRow(mergedSelectedRows);
73
75
  }
74
- const filteredRows = selectedRows.filter((selectedRow) => !rows.find((row) => row[keyId] === selectedRow[keyId]));
75
- onSelectRow(filteredRows);
76
+ const renderRowsIds = new Set(renderRows.map((row) => row[keyId]));
77
+ onSelectRow(selectedRowsRef.current.filter((selectedRow) => !renderRowsIds.has(selectedRow[keyId])));
76
78
  };
77
79
  const handleSelectRow = useCallback((row) => (event) => {
78
80
  if (!onSelectRow) {
@@ -141,6 +143,9 @@ export const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, se
141
143
  },
142
144
  bodyProps: {
143
145
  isSelectable,
146
+ selectedIds,
147
+ rowsDisabled,
148
+ getRowDisabled,
144
149
  onSelectRow: handleSelectRow,
145
150
  },
146
151
  mobileDisplayVariant,
@@ -0,0 +1,31 @@
1
+ import type { DataGridRowDisabled } from '../../types';
2
+ type GetRowDisabledStateParams = {
3
+ /**
4
+ * Итоговая блокировка строки, рассчитанная resolveRowDisabled
5
+ */
6
+ rowDisabled?: DataGridRowDisabled;
7
+ /**
8
+ * Если true, в строке выполняется блокирующее действие DataGridActionCell
9
+ */
10
+ hasBlockingAction?: boolean;
11
+ /**
12
+ * Причина блокировки от блокирующего действия
13
+ */
14
+ blockingActionReason?: string;
15
+ /**
16
+ * Значение options.isDisabledLastCell. Влияет только на блокировку от DataGridActionCell:
17
+ * options.isDisabled уже преобразован в mode в resolveRowDisabled
18
+ */
19
+ isDisabledLastCellOption?: boolean;
20
+ };
21
+ /**
22
+ * Вычисляет доступность строки и её checkbox по режиму блокировки и блокирующему действию
23
+ */
24
+ export declare const getRowDisabledState: ({ rowDisabled, hasBlockingAction, blockingActionReason, isDisabledLastCellOption, }: GetRowDisabledStateParams) => {
25
+ isDisabled: boolean;
26
+ isDisabledLastCell: boolean;
27
+ disabledReason: string | undefined;
28
+ isCheckboxDisabled: boolean;
29
+ checkboxDisabledReason: string | undefined;
30
+ };
31
+ export {};
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Вычисляет доступность строки и её checkbox по режиму блокировки и блокирующему действию
3
+ */
4
+ export const getRowDisabledState = ({ rowDisabled, hasBlockingAction = false, blockingActionReason, isDisabledLastCellOption = true, }) => {
5
+ const mode = rowDisabled?.mode;
6
+ const isSelectionDisabled = mode === 'selection';
7
+ const isDisabled = hasBlockingAction || mode === 'row' || mode === 'row-except-last-cell';
8
+ const disabledReason = rowDisabled?.reason || blockingActionReason;
9
+ return {
10
+ isDisabled,
11
+ // Без режима сохраняется прежнее поведение: isDisabledLastCell действует и при блокирующем действии
12
+ isDisabledLastCell: mode
13
+ ? mode !== 'row-except-last-cell'
14
+ : isDisabledLastCellOption,
15
+ disabledReason,
16
+ isCheckboxDisabled: isDisabled || isSelectionDisabled,
17
+ // У полностью заблокированной строки причину показывает tooltip строки
18
+ checkboxDisabledReason: isSelectionDisabled ? disabledReason : undefined,
19
+ };
20
+ };
@@ -0,0 +1 @@
1
+ export { getRowDisabledState } from './getRowDisabledState';
@@ -0,0 +1 @@
1
+ export { getRowDisabledState } from './getRowDisabledState';
@@ -3,3 +3,6 @@ export { getGridTemplateColumns } from './getGridTemplateColumns';
3
3
  export { isGroupColumns } from './isGroupColumns';
4
4
  export { getFlattenColumns } from './getFlattenColumns';
5
5
  export { checkIsMassActionsOpen } from './checkIsMassActionsOpen';
6
+ export { getRowDisabledState } from './getRowDisabledState';
7
+ export { resolveRowDisabled } from './resolveRowDisabled';
8
+ export { resolveRowsDisabled } from './resolveRowsDisabled';
@@ -3,3 +3,6 @@ export { getGridTemplateColumns } from './getGridTemplateColumns';
3
3
  export { isGroupColumns } from './isGroupColumns';
4
4
  export { getFlattenColumns } from './getFlattenColumns';
5
5
  export { checkIsMassActionsOpen } from './checkIsMassActionsOpen';
6
+ export { getRowDisabledState } from './getRowDisabledState';
7
+ export { resolveRowDisabled } from './resolveRowDisabled';
8
+ export { resolveRowsDisabled } from './resolveRowsDisabled';
@@ -0,0 +1 @@
1
+ export { resolveRowDisabled } from './resolveRowDisabled';
@@ -0,0 +1 @@
1
+ export { resolveRowDisabled } from './resolveRowDisabled';
@@ -0,0 +1,5 @@
1
+ import type { DataGridProps, DataGridRowDisabled, DataGridRowWithOptions } from '../../types';
2
+ /**
3
+ * Возвращает блокировку строки: результат getRowDisabled, а если его нет — блокировку из deprecated-полей options (isDisabled, isDisabledLastCell, disabledReason)
4
+ */
5
+ export declare const resolveRowDisabled: <TData extends Record<string, unknown>>(row: DataGridRowWithOptions<TData>, getRowDisabled?: ((row: TData) => DataGridRowDisabled | undefined) | undefined) => DataGridRowDisabled | undefined;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Возвращает блокировку строки: результат getRowDisabled, а если его нет — блокировку из deprecated-полей options (isDisabled, isDisabledLastCell, disabledReason)
3
+ */
4
+ export const resolveRowDisabled = (row, getRowDisabled) => {
5
+ const rowDisabled = getRowDisabled?.(row);
6
+ if (rowDisabled) {
7
+ return rowDisabled;
8
+ }
9
+ const { isDisabled, isDisabledLastCell = true, disabledReason = '', } = row.options ?? {};
10
+ if (!isDisabled) {
11
+ return undefined;
12
+ }
13
+ return {
14
+ mode: isDisabledLastCell ? 'row' : 'row-except-last-cell',
15
+ reason: disabledReason,
16
+ };
17
+ };
@@ -0,0 +1 @@
1
+ export { resolveRowsDisabled } from './resolveRowsDisabled';
@@ -0,0 +1 @@
1
+ export { resolveRowsDisabled } from './resolveRowsDisabled';
@@ -0,0 +1,16 @@
1
+ import type { DataGridProps, DataGridRowDisabled, DataGridRowWithOptions } from '../../types';
2
+ type ResolveRowsDisabledResult = {
3
+ /**
4
+ * Блокировки строк по row[keyId]
5
+ */
6
+ rowsDisabled: Map<unknown, DataGridRowDisabled | undefined>;
7
+ /**
8
+ * Идентификаторы строк, для которых getRowDisabled вернул блокировку без причины
9
+ */
10
+ idsWithoutReason: unknown[];
11
+ };
12
+ /**
13
+ * Вычисляет блокировки строк
14
+ */
15
+ export declare const resolveRowsDisabled: <TData extends Record<string, unknown>>(rows: DataGridRowWithOptions<TData>[], keyId: keyof TData, getRowDisabled?: ((row: TData) => DataGridRowDisabled | undefined) | undefined) => ResolveRowsDisabledResult;
16
+ export {};
@@ -0,0 +1,17 @@
1
+ import { resolveRowDisabled } from '../resolveRowDisabled';
2
+ /**
3
+ * Вычисляет блокировки строк
4
+ */
5
+ export const resolveRowsDisabled = (rows, keyId, getRowDisabled) => {
6
+ const rowsDisabled = new Map();
7
+ const idsWithoutReason = [];
8
+ rows.forEach((row) => {
9
+ const rowId = row[keyId];
10
+ const rowDisabled = getRowDisabled?.(row);
11
+ if (rowDisabled && !rowDisabled.reason) {
12
+ idsWithoutReason.push(rowId);
13
+ }
14
+ rowsDisabled.set(rowId, rowDisabled ?? resolveRowDisabled(row));
15
+ });
16
+ return { rowsDisabled, idsWithoutReason };
17
+ };
@@ -5,16 +5,16 @@ import { ActionCellContext, } from '../../ActionCellContext';
5
5
  * хук позволяющий подписаться на событие блокировки действий
6
6
  */
7
7
  export const useBlockingActions = (actions) => {
8
- const { addDisabledRow, removeDisabledRow } = useContext(RowContext);
8
+ const { addBlockingAction, removeBlockingAction } = useContext(RowContext);
9
9
  const { addDisabledCell, removeDisabledCell, isDisabled, onActionClick } = useContext(ActionCellContext);
10
10
  const blockingAction = [...(actions || [])].find((action) => action.isBlockingOperation && action.loading);
11
11
  useEffect(() => {
12
12
  if (blockingAction) {
13
- addDisabledRow(blockingAction?.loadingNote);
13
+ addBlockingAction(blockingAction?.loadingNote);
14
14
  addDisabledCell(blockingAction?.loadingNote);
15
15
  return;
16
16
  }
17
- removeDisabledRow();
17
+ removeBlockingAction();
18
18
  removeDisabledCell();
19
19
  }, [blockingAction]);
20
20
  return {
@@ -12,8 +12,9 @@ export declare const useLogic: ({ initialOpen, onOpen, onClose }: UseLogicParams
12
12
  keyId: unknown;
13
13
  columns: import("../..").DataGridColumns<Record<string, unknown>>[];
14
14
  rowId: string;
15
- isDisabled: boolean;
16
- disabledReason: string | undefined;
15
+ hasBlockingAction: boolean;
16
+ blockingActionReason: string | undefined;
17
+ rowDisabled: import("../..").DataGridRowDisabled | undefined;
17
18
  onSelectRow: (row: Record<string, unknown>) => (event: import("react").ChangeEvent<HTMLInputElement, Element>) => void;
18
19
  onRowEnter: ((rowId: string) => void) | undefined;
19
20
  onRowLeave: ((rowId: string, event: PointerEvent) => void) | undefined;
@@ -5,7 +5,7 @@ import { RowContext } from '../../DataGrid/Row/RowContext';
5
5
  import { STORAGE_KEY_PREFIX } from '../constants';
6
6
  export const useLogic = ({ initialOpen, onOpen, onClose }) => {
7
7
  const { keyId, columns, gridColumns, activeRowId, emptyCellValue, onRowClick, mobileDisplayVariant, } = useContext(DataGridContext);
8
- const { row, onSelectRow, isDisabled, disabledReason, onRowEnter, onRowLeave, registerRowElement, unregisterRowElement, isSelectable, itemProps, isSelected, } = useContext(RowContext);
8
+ const { row, onSelectRow, hasBlockingAction, blockingActionReason, onRowEnter, onRowLeave, registerRowElement, unregisterRowElement, isSelectable, itemProps, isSelected, rowDisabled, } = useContext(RowContext);
9
9
  const rowId = row[keyId];
10
10
  /**
11
11
  * Используется локальное хранилище DataGridStorageContext. Для сохранения состояния раскрытия строки на время жизни DataGrid.
@@ -46,8 +46,9 @@ export const useLogic = ({ initialOpen, onOpen, onClose }) => {
46
46
  keyId,
47
47
  columns,
48
48
  rowId,
49
- isDisabled,
50
- disabledReason,
49
+ hasBlockingAction,
50
+ blockingActionReason,
51
+ rowDisabled,
51
52
  onSelectRow,
52
53
  onRowEnter,
53
54
  onRowLeave,
@@ -49,13 +49,13 @@ const dataGridInfiniteCva = cva(dataGridClassnames.root, {
49
49
  });
50
50
  export const DataGridInfinite = (props) => {
51
51
  const { imagesMap, emptySymbol } = useContext(ConfigContext);
52
- const { isNoData, isDataGridDisabled, treeRenderConfig, virtuosoProps, headProps, rowProps, scrollToTopButtonProps, isHideHead, containerCssVars, isMobile, mobileSkeletonProps, hasGroupedColumns, gridViewMode, gridColumns, handleSelectRow, mobileDisplayVariant, isSelfContained, isNotRenderHeader, initialTopMostItemIndex, hasMassActions, isMassActionsOpen, footerContext, } = useLogic(props);
52
+ const { isNoData, isDataGridDisabled, treeRenderConfig, virtuosoProps, headProps, rowProps, scrollToTopButtonProps, isHideHead, containerCssVars, isMobile, mobileSkeletonProps, hasGroupedColumns, gridViewMode, gridColumns, handleSelectRow, mobileDisplayVariant, isSelfContained, isNotRenderHeader, initialTopMostItemIndex, hasMassActions, isMassActionsOpen, virtuosoContext, } = useLogic(props);
53
53
  const { columns, rows = [], selectedRows = [], onSelectRow, sorting, isLoading, isDisabled, keyId, emptyCellValue = emptySymbol, variant = Variant.Tree, subrows, className, isError, errorMsg, noDataPlaceholder, onRowClick, onSort, onRetry, headPreAddon, activeRowId, isTabularNums, renderRow, renderLink, massActions, } = props;
54
54
  const { moreButtonColumnPosition = 1, isVisibleCollapseButton = true } = subrows || {};
55
55
  const { isInitialExpanded = false, expandedLevel = EXPANDED_LEVEL_BY_DEFAULT, initialVisibleChildrenCount = INITIAL_OPENED_NESTED_CHILDREN_COUNT_BY_DEFAULT, } = treeRenderConfig || {};
56
56
  const { isSelectable } = rowProps;
57
57
  const TableContainer = isDisabled ? DisabledDataGridWrapper : DataGridWrapper;
58
- return (_jsx(DataGridInfiniteContextProvider, { renderRow: renderRow, onSelectRow: handleSelectRow, variant: variant, isInitialExpanded: isInitialExpanded, expandedLevel: expandedLevel, initialVisibleChildrenCount: initialVisibleChildrenCount, isSelectable: isSelectable, selectedRows: selectedRows, moreButtonColumnPosition: moreButtonColumnPosition, isVisibleCollapseButton: isVisibleCollapseButton, children: _jsx(DataGridContextProvider, { gridColumns: gridColumns, emptyCellValue: emptyCellValue, activeRowId: activeRowId, columns: columns, keyId: keyId, isTabularNums: isTabularNums, onRowClick: onRowClick, mobileDisplayVariant: mobileDisplayVariant, renderLink: renderLink, selectedRows: selectedRows, onSelectRow: onSelectRow, isMassActionsOpen: isMassActionsOpen, children: _jsxs(StyledContainer, { style: containerCssVars, className: cn(dataGridInfiniteCva({
58
+ return (_jsx(DataGridInfiniteContextProvider, { renderRow: renderRow, onSelectRow: handleSelectRow, variant: variant, isInitialExpanded: isInitialExpanded, expandedLevel: expandedLevel, initialVisibleChildrenCount: initialVisibleChildrenCount, isSelectable: isSelectable, moreButtonColumnPosition: moreButtonColumnPosition, isVisibleCollapseButton: isVisibleCollapseButton, children: _jsx(DataGridContextProvider, { gridColumns: gridColumns, emptyCellValue: emptyCellValue, activeRowId: activeRowId, columns: columns, keyId: keyId, isTabularNums: isTabularNums, onRowClick: onRowClick, mobileDisplayVariant: mobileDisplayVariant, renderLink: renderLink, selectedRows: selectedRows, onSelectRow: onSelectRow, isMassActionsOpen: isMassActionsOpen, children: _jsxs(StyledContainer, { style: containerCssVars, className: cn(dataGridInfiniteCva({
59
59
  hasGroupedColumns,
60
60
  gridViewMode,
61
61
  mobileDisplayVariant,
@@ -69,7 +69,7 @@ export const DataGridInfinite = (props) => {
69
69
  onRetry,
70
70
  }, children: _jsx(Virtuoso, { ...virtuosoProps, ...(initialTopMostItemIndex && {
71
71
  initialTopMostItemIndex,
72
- }), style: { height: '100%' }, overscan: OVERSCAN_COUNT, data: rows, context: footerContext, components: {
72
+ }), style: { height: '100%' }, overscan: OVERSCAN_COUNT, data: rows, context: virtuosoContext, components: {
73
73
  // biome-ignore lint/suspicious/noTsIgnore: Требует HTMLDivElement, а для элемента списка используем HTMLUListElement (https://github.com/petyosi/react-virtuoso/issues/864)
74
74
  // @ts-ignore
75
75
  List,
@@ -80,7 +80,7 @@ export const DataGridInfinite = (props) => {
80
80
  if (isNotRenderHeader) {
81
81
  return null;
82
82
  }
83
- return (_jsxs(HeaderRow, { children: [isLoading && _jsx(Loader, {}), isError && _jsx(ErrorMessage, { onRetry: onRetry })] }));
83
+ return (_jsxs(HeaderRow, { className: dataGridInfiniteClassnames.header, children: [isLoading && _jsx(Loader, {}), isError && _jsx(ErrorMessage, { onRetry: onRetry })] }));
84
84
  },
85
85
  } }) }), massActions && (_jsx(MassActionsAnchor, { children: massActions })), _jsx(StyledScrollToTopButton, { ...scrollToTopButtonProps, className: dataGridInfiniteClassnames.scrollToTopButton }), isDisabled && _jsx(Backdrop, {})] })] }) }) }));
86
86
  };
@@ -9,7 +9,6 @@ export type DataGridInfiniteContextProps = {
9
9
  moreButtonColumnPosition?: number;
10
10
  isVisibleCollapseButton?: boolean;
11
11
  isSelectable?: boolean;
12
- selectedRows?: Record<string, unknown>[];
13
12
  };
14
13
  /**
15
14
  * Контекст общих параметров для компонента `Item` внутри `DataGridInfinite`.
@@ -19,5 +18,6 @@ export type DataGridInfiniteContextProps = {
19
18
  * лишние перерисовки виртуализированного списка.
20
19
  *
21
20
  * Поскольку `Virtuoso` сам управляет параметрами `Item`, общие настройки передаются через контекст.
21
+ * Состояние строк передаётся отдельно, см. `DataGridInfiniteItemContext` в `Row/types.ts`.
22
22
  */
23
23
  export declare const DataGridInfiniteContext: import("react").Context<DataGridInfiniteContextProps>;
@@ -7,6 +7,7 @@ import { createContext } from 'react';
7
7
  * лишние перерисовки виртуализированного списка.
8
8
  *
9
9
  * Поскольку `Virtuoso` сам управляет параметрами `Item`, общие настройки передаются через контекст.
10
+ * Состояние строк передаётся отдельно, см. `DataGridInfiniteItemContext` в `Row/types.ts`.
10
11
  */
11
12
  export const DataGridInfiniteContext = createContext({
12
13
  renderRow: () => null,
@@ -17,5 +18,4 @@ export const DataGridInfiniteContext = createContext({
17
18
  initialVisibleChildrenCount: 0,
18
19
  moreButtonColumnPosition: 0,
19
20
  isVisibleCollapseButton: true,
20
- selectedRows: [],
21
21
  });
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Loader } from '../../Loader';
3
3
  import { dataGridInfiniteClassnames } from '../constants';
4
4
  import { EndData } from '../EndData';
@@ -21,5 +21,5 @@ export const Footer = ({ context }) => {
21
21
  }
22
22
  return null;
23
23
  };
24
- return (_jsxs(_Fragment, { children: [renderStatus(), hasMassActions && (_jsx(MassActionsSpacer, { className: dataGridInfiniteClassnames.massActionsSpacer }))] }));
24
+ return (_jsxs("div", { className: dataGridInfiniteClassnames.footer, children: [renderStatus(), hasMassActions && (_jsx(MassActionsSpacer, { className: dataGridInfiniteClassnames.massActionsSpacer }))] }));
25
25
  };
@@ -5,6 +5,6 @@ import { RowContextProvider } from '../../DataGrid/Row/RowContext';
5
5
  import { useLogic } from './useLogic';
6
6
  const INITIAL_LEVEL = 0;
7
7
  export const Row = ({ children, ...props }) => {
8
- const { item, memoizedItemProps, onSelectRow, renderRow, flattenColumns, gridColumns, options, keyId, variant, isSelectable, isSelected, nestedChildren, isInitialExpanded, expandedLevel, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, emptyCellValue, activeRowId, onRowClick, } = useLogic(props);
9
- return (_jsx(RowContextProvider, { row: item, itemProps: memoizedItemProps, onSelectRow: onSelectRow, isSelectable: isSelectable, isSelected: isSelected, children: renderRow ? (_jsx(CustomRowWrapper, { index: memoizedItemProps['data-index'], renderRow: renderRow, row: item })) : (_jsx(RowComponent, { ...memoizedItemProps, row: item, columns: flattenColumns, gridColumns: gridColumns, options: options, keyId: keyId, variant: variant, isSelectable: isSelectable, isSelected: isSelected, level: INITIAL_LEVEL, nestedChildren: nestedChildren, isInitialExpanded: isInitialExpanded || false, expandedLevel: expandedLevel || 1, initialVisibleChildrenCount: initialVisibleChildrenCount || 2, activeRowId: activeRowId, moreButtonColumnPosition: moreButtonColumnPosition || 1, isVisibleCollapseButton: isVisibleCollapseButton || true, emptyCellValue: emptyCellValue, onRowClick: onRowClick, onSelectRow: onSelectRow })) }));
8
+ const { item, memoizedItemProps, onSelectRow, renderRow, flattenColumns, gridColumns, options, keyId, variant, isSelectable, isSelected, disabledMode, disabledReason, getRowDisabled, nestedChildren, isInitialExpanded, expandedLevel, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, emptyCellValue, activeRowId, onRowClick, } = useLogic(props);
9
+ return (_jsx(RowContextProvider, { row: item, itemProps: memoizedItemProps, onSelectRow: onSelectRow, isSelectable: isSelectable, isSelected: isSelected, disabledMode: disabledMode, disabledReason: disabledReason, children: renderRow ? (_jsx(CustomRowWrapper, { index: memoizedItemProps['data-index'], renderRow: renderRow, row: item })) : (_jsx(RowComponent, { ...memoizedItemProps, row: item, columns: flattenColumns, gridColumns: gridColumns, options: options, keyId: keyId, variant: variant, isSelectable: isSelectable, isSelected: isSelected, getRowDisabled: getRowDisabled, level: INITIAL_LEVEL, nestedChildren: nestedChildren, isInitialExpanded: isInitialExpanded || false, expandedLevel: expandedLevel || 1, initialVisibleChildrenCount: initialVisibleChildrenCount || 2, activeRowId: activeRowId, moreButtonColumnPosition: moreButtonColumnPosition || 1, isVisibleCollapseButton: isVisibleCollapseButton || true, emptyCellValue: emptyCellValue, onRowClick: onRowClick, onSelectRow: onSelectRow })) }));
10
10
  };
@@ -1 +1,2 @@
1
1
  export * from './Row';
2
+ export type { DataGridInfiniteItemContext } from './types';
@@ -1,2 +1,17 @@
1
- import type { ItemProps as VirtuosoItemProps } from 'react-virtuoso';
2
- export type RowProps = VirtuosoItemProps<Record<string, unknown>>;
1
+ import type { ContextProp, ItemProps as VirtuosoItemProps } from 'react-virtuoso';
2
+ import { type DataGridProps, type DataGridRowDisabled } from '../../DataGrid/types';
3
+ type ContextGetRowDisabled = DataGridProps<Record<string, unknown>>['getRowDisabled'];
4
+ /**
5
+ * Состояние строк.
6
+ *
7
+ * Передаётся через `context` `Virtuoso`, а не через React-контекст: `Virtuoso` отдаёт `Item` новые данные
8
+ * на рендер позже родителя. Состояние из React-контекста приходило бы раньше данных, и строка
9
+ * перерисовывалась бы дважды — сначала со старыми данными и новым состоянием, затем с новыми данными.
10
+ */
11
+ export type DataGridInfiniteItemContext = {
12
+ selectedIds: Set<unknown>;
13
+ rowsDisabled: Map<unknown, DataGridRowDisabled | undefined>;
14
+ getRowDisabled?: ContextGetRowDisabled;
15
+ };
16
+ export type RowProps = VirtuosoItemProps<Record<string, unknown>> & ContextProp<DataGridInfiniteItemContext>;
17
+ export {};
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import { type RowProps } from '../types';
3
3
  type UseLogicParams = Omit<RowProps, 'children'>;
4
- export declare const useLogic: ({ item, ...itemProps }: UseLogicParams) => {
4
+ export declare const useLogic: ({ item, context, ...itemProps }: UseLogicParams) => {
5
5
  item: Record<string, unknown>;
6
6
  renderRow: (row: Record<string, unknown>, index: number) => import("react").ReactNode;
7
7
  isSelected: boolean;
@@ -21,7 +21,9 @@ export declare const useLogic: ({ item, ...itemProps }: UseLogicParams) => {
21
21
  moreButtonColumnPosition: number | undefined;
22
22
  isVisibleCollapseButton: boolean | undefined;
23
23
  isSelectable: boolean | undefined;
24
- selectedRows: Record<string, unknown>[] | undefined;
24
+ disabledMode: "row" | "selection" | "row-except-last-cell" | undefined;
25
+ disabledReason: string | undefined;
26
+ getRowDisabled: ((row: Record<string, unknown>) => import("../../..").DataGridRowDisabled | undefined) | undefined;
25
27
  memoizedItemProps: {
26
28
  style?: import("react").CSSProperties | undefined;
27
29
  'data-index': number;
@@ -2,13 +2,18 @@ import { useContext, useMemo } from 'react';
2
2
  import { DataGridContext } from '../../../DataGrid/DataGridContext';
3
3
  import { getFlattenColumns } from '../../../DataGrid/utils';
4
4
  import { DataGridInfiniteContext } from '../../DataGridInfiniteContext';
5
- export const useLogic = ({ item, ...itemProps }) => {
6
- const { renderRow, onSelectRow, variant, isInitialExpanded, expandedLevel, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, isSelectable, selectedRows, } = useContext(DataGridInfiniteContext);
5
+ export const useLogic = ({ item, context, ...itemProps }) => {
6
+ const { renderRow, onSelectRow, variant, isInitialExpanded, expandedLevel, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, isSelectable, } = useContext(DataGridInfiniteContext);
7
+ // Состояние строки приходит от Virtuoso вместе с item, см. DataGridInfiniteItemContext
8
+ const { selectedIds, rowsDisabled, getRowDisabled } = context;
7
9
  const { activeRowId, columns, emptyCellValue, onRowClick, keyId, gridColumns, } = useContext(DataGridContext);
8
10
  const memoizedItemProps = useMemo(() => itemProps, [itemProps.style]);
9
11
  const flattenColumns = useMemo(() => getFlattenColumns(columns), [columns]);
10
12
  const { children: nestedChildren, options } = item;
11
- const isSelected = Boolean(selectedRows?.find((selectedRow) => selectedRow[keyId] === item[keyId]));
13
+ const rowId = item[keyId];
14
+ const isSelected = selectedIds.has(rowId);
15
+ const rowDisabled = rowsDisabled.get(rowId);
16
+ const hasNestedChildren = Array.isArray(nestedChildren) && nestedChildren.length > 0;
12
17
  return {
13
18
  item,
14
19
  renderRow,
@@ -29,7 +34,10 @@ export const useLogic = ({ item, ...itemProps }) => {
29
34
  moreButtonColumnPosition,
30
35
  isVisibleCollapseButton,
31
36
  isSelectable,
32
- selectedRows,
37
+ disabledMode: rowDisabled?.mode,
38
+ disabledReason: rowDisabled?.reason,
39
+ // Функция нужна только для расчёта блокировки вложенных строк, остальным не передаём, чтобы не сбрасывать memo
40
+ getRowDisabled: hasNestedChildren ? getRowDisabled : undefined,
33
41
  memoizedItemProps,
34
42
  };
35
43
  };
@@ -6,4 +6,6 @@ export declare const dataGridInfiniteClassnames: {
6
6
  scrollToTopButton: string;
7
7
  visibleScrollToTopButton: string;
8
8
  massActionsSpacer: string;
9
+ header: string;
10
+ footer: string;
9
11
  };
@@ -7,4 +7,6 @@ export const dataGridInfiniteClassnames = {
7
7
  scrollToTopButton: createUIKitClassname('data-grid-infinite__scroll-to-top-button'),
8
8
  visibleScrollToTopButton: createUIKitClassname('data-grid-infinite__scroll-to-top-button_visible'),
9
9
  massActionsSpacer: createUIKitClassname('data-grid-infinite__mass-actions-spacer'),
10
+ header: createUIKitClassname('data-grid-infinite__header'),
11
+ footer: createUIKitClassname('data-grid-infinite__footer'),
10
12
  };
@@ -7,11 +7,7 @@ type DataType = {
7
7
  actions?: object;
8
8
  };
9
9
  export declare const makeRandomDate: () => string;
10
- export declare const makeDataList: (length?: number, options?: {
11
- isDisabled?: boolean;
12
- isDisabledLastCell?: boolean;
13
- disabledReason?: string;
14
- }) => DataType[];
10
+ export declare const makeDataList: (length?: number) => DataType[];
15
11
  export declare const makeDataListWithTree: (length?: number, options?: {
16
12
  isNotSelectable?: boolean;
17
13
  childrenCount?: number;
@@ -11,13 +11,12 @@ export const makeRandomDate = () => {
11
11
  });
12
12
  return randomDate.toISOString();
13
13
  };
14
- export const makeDataList = (length = 10, options) => {
14
+ export const makeDataList = (length = 10) => {
15
15
  return Array.from({ length }).map((_, i) => ({
16
16
  id: i.toString(),
17
17
  documentName: `Договор №${i + 1}`,
18
18
  recipient: RECIPIENTS[faker.number.int({ min: 0, max: RECIPIENTS.length })],
19
19
  createDate: makeRandomDate(),
20
- options: Math.random() < 0.4 ? options : {},
21
20
  }));
22
21
  };
23
22
  export const makeDataListWithTree = (length = 10, options) => {
@@ -3,10 +3,11 @@ import type { ListRange } from 'react-virtuoso';
3
3
  import { type DataGridRow } from '../../DataGrid';
4
4
  import { type CellValue } from '../../DataGrid/types';
5
5
  import { type FooterContext } from '../Footer';
6
+ import { type DataGridInfiniteItemContext } from '../Row';
6
7
  import { type DataGridInfiniteProps, type DataGridInfiniteVirtuosoHandle } from '../types';
7
8
  type UseLogicParams<TData extends Record<string, CellValue> = DataGridRow, TSortField extends keyof TData = keyof TData> = DataGridInfiniteProps<TData, TSortField>;
8
- export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isError, isDisabled, isEndReached, endOfScrollMsg, onRetry, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, massActions, mobileDisplayVariant, virtuosoRef, }: UseLogicParams<TData, TSortField>) => {
9
- footerContext: FooterContext;
9
+ export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isError, isDisabled, getRowDisabled, isEndReached, endOfScrollMsg, onRetry, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, massActions, mobileDisplayVariant, virtuosoRef, }: UseLogicParams<TData, TSortField>) => {
10
+ virtuosoContext: DataGridInfiniteItemContext & FooterContext;
10
11
  hasMassActions: boolean;
11
12
  isMassActionsOpen: boolean;
12
13
  containerCssVars: import("react").CSSProperties;
@@ -1,13 +1,13 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
2
2
  import { EXPANDED_LEVEL_BY_DEFAULT, INITIAL_OPENED_NESTED_CHILDREN_COUNT_ON_MOBILE_BY_DEFAULT, } from '../../DataGrid/constants';
3
3
  import { Variant } from '../../DataGrid/enums';
4
+ import { useWarnRowsWithoutDisabledReason } from '../../DataGrid/hooks';
4
5
  import { useDataGridMobileLogic } from '../../DataGrid/useLogic/hooks';
5
- import { checkIsMassActionsOpen, getDataGridCssVars, getFlattenColumns, getGridTemplateColumns, isGroupColumns, } from '../../DataGrid/utils';
6
+ import { checkIsMassActionsOpen, getDataGridCssVars, getFlattenColumns, getGridTemplateColumns, isGroupColumns, resolveRowsDisabled, } from '../../DataGrid/utils';
6
7
  import { useViewportType } from '../../useViewportType';
7
- import { prop } from '../../utils/prop';
8
8
  import { uniqueBy } from '../../utils/uniqueBy';
9
9
  import { dataGridInfiniteClassnames, VIRTUOSO_CONTAINER_ID, } from '../constants';
10
- export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isError, isDisabled, isEndReached, endOfScrollMsg, onRetry, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', massActions, mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', virtuosoRef, }) => {
10
+ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isError, isDisabled, getRowDisabled, isEndReached, endOfScrollMsg, onRetry, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', massActions, mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', virtuosoRef, }) => {
11
11
  const virtuoso = useRef(null);
12
12
  const handleVirtuosoRef = useCallback((node) => {
13
13
  virtuoso.current = node;
@@ -66,7 +66,16 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
66
66
  }
67
67
  return config;
68
68
  }, [variant, subrows, tree, isMobile]);
69
- const availableRows = rows.filter((row) => !(row.options?.isDisabled || row.options?.isNotSelectable));
69
+ // Блокировки строк: getRowDisabled вызывается один раз на строку
70
+ const { rowsDisabled, idsWithoutReason } = useMemo(() => resolveRowsDisabled(rows, keyId, getRowDisabled), [rows, keyId, getRowDisabled]);
71
+ useWarnRowsWithoutDisabledReason(idsWithoutReason);
72
+ const selectedIds = useMemo(() => new Set(selectedRows.map((row) => row[keyId])), [selectedRows, keyId]);
73
+ const itemContext = useMemo(() => ({
74
+ selectedIds,
75
+ rowsDisabled,
76
+ getRowDisabled: getRowDisabled,
77
+ }), [selectedIds, rowsDisabled, getRowDisabled]);
78
+ const availableRows = useMemo(() => rows.filter((row) => !row.options?.isNotSelectable && !rowsDisabled.get(row[keyId])), [rows, rowsDisabled, keyId]);
70
79
  const flattenColumns = useMemo(() => getFlattenColumns(columns), [columns]);
71
80
  const hasGroupedColumns = columns.some(isGroupColumns);
72
81
  const gridViewMode = hasGroupedColumns ? 'full' : gridViewModeProp;
@@ -81,9 +90,7 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
81
90
  onStartReached();
82
91
  }
83
92
  }, [isStartReached, onStartReached]);
84
- const uncheckedRowsCount = useMemo(() => {
85
- return availableRows.filter((row) => !selectedRows.find((selectedRow) => selectedRow[keyId] === row[keyId])).length;
86
- }, [availableRows, selectedRows, keyId]);
93
+ const uncheckedRowsCount = useMemo(() => availableRows.filter((row) => !selectedIds.has(row[keyId])).length, [availableRows, selectedIds, keyId]);
87
94
  const handleRangeChanged = useCallback((range) => {
88
95
  if (scrollToTopButtonRef.current) {
89
96
  scrollToTopButtonRef.current.classList.toggle(dataGridInfiniteClassnames.visibleScrollToTopButton, range.startIndex > baseIndex.current + 2);
@@ -94,11 +101,11 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
94
101
  return;
95
102
  }
96
103
  if (event.target.checked) {
97
- const mergedSelectedRows = uniqueBy([...selectedRows, ...availableRows], prop(keyId));
104
+ const mergedSelectedRows = uniqueBy([...selectedRowsRef.current, ...availableRows], (row) => row[keyId]);
98
105
  return onSelectRow(mergedSelectedRows);
99
106
  }
100
- const filteredRows = selectedRows.filter((selectedRow) => !rows.find((row) => row[keyId] === selectedRow[keyId]));
101
- onSelectRow(filteredRows);
107
+ const rowsIds = new Set(rows.map((row) => row[keyId]));
108
+ onSelectRow(selectedRowsRef.current.filter((selectedRow) => !rowsIds.has(selectedRow[keyId])));
102
109
  };
103
110
  const handleSelectRow = useCallback((row) => (event) => {
104
111
  if (!onSelectRow) {
@@ -115,8 +122,12 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
115
122
  const recalculateVerticalScroll = (listHeight) => {
116
123
  const container = document.getElementById(VIRTUOSO_CONTAINER_ID);
117
124
  if (container) {
118
- const massActionsSpacerHeight = container.querySelector(`.${dataGridInfiniteClassnames.massActionsSpacer}`)?.clientHeight ?? 0;
119
- setHasVerticalScroll(listHeight - massActionsSpacerHeight > container.clientHeight);
125
+ // listHeight включает header и footer
126
+ const getHeight = (className) => container.querySelector(`.${className}`)?.clientHeight ?? 0;
127
+ const rowsHeight = listHeight -
128
+ getHeight(dataGridInfiniteClassnames.header) -
129
+ getHeight(dataGridInfiniteClassnames.footer);
130
+ setHasVerticalScroll(rowsHeight > container.clientHeight);
120
131
  }
121
132
  };
122
133
  const isHideHead = isHideHeaderIfNoData && rows.length === 0;
@@ -152,8 +163,9 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
152
163
  onRetry,
153
164
  hasMassActions,
154
165
  ]);
166
+ const virtuosoContext = useMemo(() => ({ ...itemContext, ...footerContext }), [itemContext, footerContext]);
155
167
  return {
156
- footerContext,
168
+ virtuosoContext,
157
169
  hasMassActions,
158
170
  isMassActionsOpen,
159
171
  containerCssVars,