@astral/ui 4.2.2 → 4.2.3

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.
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useContext, useMemo } from 'react';
2
+ import { useContext } from 'react';
3
3
  import { ConfigContext } from '../../ConfigProvider';
4
4
  import { ContentState } from '../../ContentState';
5
5
  import { classNames } from '../../utils';
@@ -13,12 +13,11 @@ export const Body = (props) => {
13
13
  const { imagesMap } = useContext(ConfigContext);
14
14
  const { isNoData, contentStateProps } = useLogic(props);
15
15
  const { rows, selectedRows = [], isLoading, isError, errorMsg, minDisplayRows, keyId, noDataPlaceholder, onRetry, ...rowProps } = props;
16
- const renderedRows = useMemo(() => {
17
- return rows.map(({ children, options, ...row }) => {
18
- const rowId = row[keyId];
19
- return (_jsx(RowContextProvider, { children: _jsx(Row, { row: row, selectedRows: selectedRows, options: options, keyId: keyId, level: INITIAL_LEVEL, nestedChildren: children, ...rowProps }) }, rowId));
20
- });
21
- }, [rows, keyId, selectedRows, rowProps]);
16
+ const renderedRows = rows.map(({ children, options, ...row }) => {
17
+ const rowId = row[keyId];
18
+ const isSelected = Boolean(selectedRows?.find((selectedRow) => selectedRow[keyId] === rowId));
19
+ return (_jsx(RowContextProvider, { children: _jsx(Row, { row: row, selectedRows: selectedRows, options: options, keyId: keyId, level: INITIAL_LEVEL, nestedChildren: children, isSelected: isSelected, ...rowProps }, rowId) }, rowId));
20
+ });
22
21
  return (_jsx(Wrapper, { className: classNames(dataGridClassnames.body, {
23
22
  [dataGridClassnames.bodyEmpty]: isNoData,
24
23
  }), "$minDisplayRows": minDisplayRows, children: _jsx(ContentState, { ...contentStateProps, errorState: {
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useCallback, useMemo, useState } from 'react';
3
3
  import { DataGridContext } from '../DataGridContext';
4
4
  const ROW_FLAGS_BY_DEFAULT = {
5
5
  isOpenedItems: true,
@@ -7,27 +7,27 @@ const ROW_FLAGS_BY_DEFAULT = {
7
7
  };
8
8
  export const DataGridContextProvider = ({ isLoading, children, }) => {
9
9
  const [openedItems, setOpenedItems] = useState({});
10
- const checkIsOpened = (key) => {
10
+ const checkIsOpened = useCallback((key) => {
11
11
  if (openedItems[key]) {
12
12
  return true;
13
13
  }
14
14
  return false;
15
- };
16
- const checkIsMoreOpened = (key) => {
15
+ }, [openedItems]);
16
+ const checkIsMoreOpened = useCallback((key) => {
17
17
  if (openedItems[key]) {
18
18
  return openedItems[key].iOpenedMoreItems;
19
19
  }
20
20
  return false;
21
- };
22
- const toggleOpenItems = (key) => setOpenedItems((currentOpenedItems) => {
21
+ }, [openedItems]);
22
+ const toggleOpenItems = useCallback((key) => setOpenedItems((currentOpenedItems) => {
23
23
  if (checkIsOpened(key)) {
24
24
  const newOpenedItems = { ...currentOpenedItems };
25
25
  delete newOpenedItems[key];
26
26
  return newOpenedItems;
27
27
  }
28
28
  return { ...currentOpenedItems, [key]: ROW_FLAGS_BY_DEFAULT };
29
- });
30
- const toggleOpenMoreItems = (key) => setOpenedItems((currentOpenedItems) => {
29
+ }), [openedItems]);
30
+ const toggleOpenMoreItems = useCallback((key) => setOpenedItems((currentOpenedItems) => {
31
31
  if (checkIsMoreOpened(key)) {
32
32
  const newOpenedItems = { ...currentOpenedItems };
33
33
  newOpenedItems[key] = ROW_FLAGS_BY_DEFAULT;
@@ -37,12 +37,20 @@ export const DataGridContextProvider = ({ isLoading, children, }) => {
37
37
  ...currentOpenedItems,
38
38
  [key]: { ...ROW_FLAGS_BY_DEFAULT, iOpenedMoreItems: true },
39
39
  };
40
- });
41
- return (_jsx(DataGridContext.Provider, { value: {
42
- checkIsOpened,
43
- checkIsMoreOpened,
44
- toggleOpenItems,
45
- toggleOpenMoreItems,
46
- isLoading,
47
- }, children: children }));
40
+ }), [openedItems]);
41
+ const contextValue = useMemo(() => ({
42
+ checkIsOpened,
43
+ checkIsMoreOpened,
44
+ toggleOpenItems,
45
+ toggleOpenMoreItems,
46
+ isLoading,
47
+ }), [
48
+ checkIsOpened,
49
+ checkIsMoreOpened,
50
+ toggleOpenItems,
51
+ toggleOpenMoreItems,
52
+ isLoading,
53
+ openedItems,
54
+ ]);
55
+ return (_jsx(DataGridContext.Provider, { value: contextValue, children: children }));
48
56
  };
@@ -66,6 +66,10 @@ export type RowProps<TData extends Record<string, CellValue>> = {
66
66
  * Если true, то будет отображаться чекбокс для выбора элемента
67
67
  */
68
68
  isSelectable?: boolean;
69
+ /**
70
+ * Флаг выбора текущей строки
71
+ */
72
+ isSelected?: boolean;
69
73
  /**
70
74
  * Массив выбранных строк
71
75
  */
@@ -1,14 +1,15 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback } from 'react';
2
+ import { memo, useCallback } from 'react';
3
3
  import { Checkbox } from '../../Checkbox';
4
4
  import { useHidePersonalData } from '../../personalDataSecurity';
5
5
  import { Tooltip } from '../../Tooltip';
6
+ import { checkIsDeepEqual } from '../../utils/external';
6
7
  import { DISABLE_ROW_ATTR } from './constants';
7
8
  import { NestedChildren } from './NestedChildren';
8
9
  import { CellStyled, CheckboxCell, ChevronIcon, CollapseButton, CollapseCell, ContentWrapper, Wrapper, } from './styles';
9
10
  import { useLogic } from './useLogic';
10
11
  import { checkIsDisabled } from './utils';
11
- export const Row = (props) => {
12
+ const RowComponent = (props) => {
12
13
  const { isOpen, isShowConnector, childrenColumns, rowId, handleToggle, checkboxProps, rowProps, tooltipProps, nestedChildrenProps, disabled, isRenderCollapseButton, } = useLogic(props);
13
14
  const { className, row, options, variant, isSelectable, gridColumns, isInitialExpanded, expandedLevel, level, nestedChildren, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, columns, emptyCellValue, selectedRows, activeRowId, keyId, onSelectRow, onRowClick,
14
15
  // В этот rest-оператор попадают специфичные пропсы (атрибуты) virtuoso
@@ -53,3 +54,27 @@ export const Row = (props) => {
53
54
  // @ts-ignore
54
55
  renderRow: renderRow })] }));
55
56
  };
57
+ const arePropsEqual = (prevProps, nextProps) => {
58
+ const deepEqualityProps = new Set(['row']);
59
+ // Список пропсов, которые нужно игнорировать при сравнении
60
+ const skipProps = new Set([
61
+ 'selectedRows',
62
+ 'onSelectRow',
63
+ 'columns',
64
+ ]);
65
+ const keys = Object.keys(prevProps);
66
+ return keys.every((key) => {
67
+ const typedKey = key;
68
+ // Не сравниваем свойства, которые нужно пропустить
69
+ if (skipProps.has(typedKey)) {
70
+ return true;
71
+ }
72
+ const prevValue = prevProps[typedKey];
73
+ const nextValue = nextProps[typedKey];
74
+ if (deepEqualityProps.has(typedKey)) {
75
+ return checkIsDeepEqual(prevValue, nextValue);
76
+ }
77
+ return prevValue === nextValue;
78
+ });
79
+ };
80
+ export const Row = memo(RowComponent, arePropsEqual);
@@ -1,21 +1,22 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useCallback, useMemo, useState } from 'react';
3
3
  import { RowContext } from '../RowContext';
4
4
  export const RowContextProvider = ({ children }) => {
5
5
  const [isDisabled, setDisabled] = useState(false);
6
6
  const [disabledReason, setDisabledReason] = useState();
7
- const addDisabledRow = (reason) => {
7
+ const addDisabledRow = useCallback((reason) => {
8
8
  setDisabled(true);
9
9
  setDisabledReason(reason);
10
- };
11
- const removeDisabledRow = () => {
10
+ }, []);
11
+ const removeDisabledRow = useCallback(() => {
12
12
  setDisabled(false);
13
13
  setDisabledReason(undefined);
14
- };
15
- return (_jsx(RowContext.Provider, { value: {
16
- isDisabled,
17
- disabledReason,
18
- addDisabledRow,
19
- removeDisabledRow,
20
- }, children: children }));
14
+ }, []);
15
+ const contextValue = useMemo(() => ({
16
+ isDisabled,
17
+ disabledReason,
18
+ addDisabledRow,
19
+ removeDisabledRow,
20
+ }), [isDisabled, disabledReason, addDisabledRow, removeDisabledRow]);
21
+ return (_jsx(RowContext.Provider, { value: contextValue, children: children }));
21
22
  };
@@ -7,6 +7,11 @@ export const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, se
7
7
  const isFirstRender = useFirstMountState();
8
8
  const isSelectable = Boolean(onSelectRow);
9
9
  const isDataGridDisabled = isLoading || isDisabled;
10
+ // Реф добавлен для избежания замыкания selectedRows в handleSelectRow (иначе в handleSelectRow приходят некорректные значения selectedRows).
11
+ const selectedRowsRef = useRef(selectedRows);
12
+ useEffect(() => {
13
+ selectedRowsRef.current = selectedRows;
14
+ }, [selectedRows]);
10
15
  const treeRenderConfig = Object.is(variant, Variant.Subrows)
11
16
  ? { ...subrows, isInitialExpanded: true }
12
17
  : tree;
@@ -40,15 +45,15 @@ export const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, se
40
45
  const filteredRows = selectedRows.filter((selectedRow) => !rows.find((row) => row[keyId] === selectedRow[keyId]));
41
46
  onSelectRow(filteredRows);
42
47
  };
43
- const handleSelectRow = useCallback((row) => (event) => {
48
+ const handleSelectRow = (row) => (event) => {
44
49
  if (!onSelectRow) {
45
50
  return;
46
51
  }
47
52
  if (event.target.checked) {
48
- return onSelectRow([...selectedRows, row]);
53
+ return onSelectRow([...selectedRowsRef.current, row]);
49
54
  }
50
- onSelectRow(selectedRows.filter((selectedRow) => selectedRow[keyId] !== row[keyId]));
51
- }, [selectedRows, onSelectRow, keyId]);
55
+ onSelectRow(selectedRowsRef.current.filter((selectedRow) => selectedRow[keyId] !== row[keyId]));
56
+ };
52
57
  return {
53
58
  isDataGridDisabled,
54
59
  treeRenderConfig,
@@ -16,12 +16,11 @@ const Body = (props) => {
16
16
  const { imagesMap } = (0, react_1.useContext)(ConfigProvider_1.ConfigContext);
17
17
  const { isNoData, contentStateProps } = (0, useLogic_1.useLogic)(props);
18
18
  const { rows, selectedRows = [], isLoading, isError, errorMsg, minDisplayRows, keyId, noDataPlaceholder, onRetry, ...rowProps } = props;
19
- const renderedRows = (0, react_1.useMemo)(() => {
20
- return rows.map(({ children, options, ...row }) => {
21
- const rowId = row[keyId];
22
- return ((0, jsx_runtime_1.jsx)(RowContext_1.RowContextProvider, { children: (0, jsx_runtime_1.jsx)(Row_1.Row, { row: row, selectedRows: selectedRows, options: options, keyId: keyId, level: INITIAL_LEVEL, nestedChildren: children, ...rowProps }) }, rowId));
23
- });
24
- }, [rows, keyId, selectedRows, rowProps]);
19
+ const renderedRows = rows.map(({ children, options, ...row }) => {
20
+ const rowId = row[keyId];
21
+ const isSelected = Boolean(selectedRows?.find((selectedRow) => selectedRow[keyId] === rowId));
22
+ return ((0, jsx_runtime_1.jsx)(RowContext_1.RowContextProvider, { children: (0, jsx_runtime_1.jsx)(Row_1.Row, { row: row, selectedRows: selectedRows, options: options, keyId: keyId, level: INITIAL_LEVEL, nestedChildren: children, isSelected: isSelected, ...rowProps }, rowId) }, rowId));
23
+ });
25
24
  return ((0, jsx_runtime_1.jsx)(styles_1.Wrapper, { className: (0, utils_1.classNames)(constants_1.dataGridClassnames.body, {
26
25
  [constants_1.dataGridClassnames.bodyEmpty]: isNoData,
27
26
  }), "$minDisplayRows": minDisplayRows, children: (0, jsx_runtime_1.jsx)(ContentState_1.ContentState, { ...contentStateProps, errorState: {
@@ -10,27 +10,27 @@ const ROW_FLAGS_BY_DEFAULT = {
10
10
  };
11
11
  const DataGridContextProvider = ({ isLoading, children, }) => {
12
12
  const [openedItems, setOpenedItems] = (0, react_1.useState)({});
13
- const checkIsOpened = (key) => {
13
+ const checkIsOpened = (0, react_1.useCallback)((key) => {
14
14
  if (openedItems[key]) {
15
15
  return true;
16
16
  }
17
17
  return false;
18
- };
19
- const checkIsMoreOpened = (key) => {
18
+ }, [openedItems]);
19
+ const checkIsMoreOpened = (0, react_1.useCallback)((key) => {
20
20
  if (openedItems[key]) {
21
21
  return openedItems[key].iOpenedMoreItems;
22
22
  }
23
23
  return false;
24
- };
25
- const toggleOpenItems = (key) => setOpenedItems((currentOpenedItems) => {
24
+ }, [openedItems]);
25
+ const toggleOpenItems = (0, react_1.useCallback)((key) => setOpenedItems((currentOpenedItems) => {
26
26
  if (checkIsOpened(key)) {
27
27
  const newOpenedItems = { ...currentOpenedItems };
28
28
  delete newOpenedItems[key];
29
29
  return newOpenedItems;
30
30
  }
31
31
  return { ...currentOpenedItems, [key]: ROW_FLAGS_BY_DEFAULT };
32
- });
33
- const toggleOpenMoreItems = (key) => setOpenedItems((currentOpenedItems) => {
32
+ }), [openedItems]);
33
+ const toggleOpenMoreItems = (0, react_1.useCallback)((key) => setOpenedItems((currentOpenedItems) => {
34
34
  if (checkIsMoreOpened(key)) {
35
35
  const newOpenedItems = { ...currentOpenedItems };
36
36
  newOpenedItems[key] = ROW_FLAGS_BY_DEFAULT;
@@ -40,13 +40,21 @@ const DataGridContextProvider = ({ isLoading, children, }) => {
40
40
  ...currentOpenedItems,
41
41
  [key]: { ...ROW_FLAGS_BY_DEFAULT, iOpenedMoreItems: true },
42
42
  };
43
- });
44
- return ((0, jsx_runtime_1.jsx)(DataGridContext_1.DataGridContext.Provider, { value: {
45
- checkIsOpened,
46
- checkIsMoreOpened,
47
- toggleOpenItems,
48
- toggleOpenMoreItems,
49
- isLoading,
50
- }, children: children }));
43
+ }), [openedItems]);
44
+ const contextValue = (0, react_1.useMemo)(() => ({
45
+ checkIsOpened,
46
+ checkIsMoreOpened,
47
+ toggleOpenItems,
48
+ toggleOpenMoreItems,
49
+ isLoading,
50
+ }), [
51
+ checkIsOpened,
52
+ checkIsMoreOpened,
53
+ toggleOpenItems,
54
+ toggleOpenMoreItems,
55
+ isLoading,
56
+ openedItems,
57
+ ]);
58
+ return ((0, jsx_runtime_1.jsx)(DataGridContext_1.DataGridContext.Provider, { value: contextValue, children: children }));
51
59
  };
52
60
  exports.DataGridContextProvider = DataGridContextProvider;
@@ -66,6 +66,10 @@ export type RowProps<TData extends Record<string, CellValue>> = {
66
66
  * Если true, то будет отображаться чекбокс для выбора элемента
67
67
  */
68
68
  isSelectable?: boolean;
69
+ /**
70
+ * Флаг выбора текущей строки
71
+ */
72
+ isSelected?: boolean;
69
73
  /**
70
74
  * Массив выбранных строк
71
75
  */
@@ -6,12 +6,13 @@ const react_1 = require("react");
6
6
  const Checkbox_1 = require("../../Checkbox");
7
7
  const personalDataSecurity_1 = require("../../personalDataSecurity");
8
8
  const Tooltip_1 = require("../../Tooltip");
9
+ const external_1 = require("../../utils/external");
9
10
  const constants_1 = require("./constants");
10
11
  const NestedChildren_1 = require("./NestedChildren");
11
12
  const styles_1 = require("./styles");
12
13
  const useLogic_1 = require("./useLogic");
13
14
  const utils_1 = require("./utils");
14
- const Row = (props) => {
15
+ const RowComponent = (props) => {
15
16
  const { isOpen, isShowConnector, childrenColumns, rowId, handleToggle, checkboxProps, rowProps, tooltipProps, nestedChildrenProps, disabled, isRenderCollapseButton, } = (0, useLogic_1.useLogic)(props);
16
17
  const { className, row, options, variant, isSelectable, gridColumns, isInitialExpanded, expandedLevel, level, nestedChildren, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, columns, emptyCellValue, selectedRows, activeRowId, keyId, onSelectRow, onRowClick,
17
18
  // В этот rest-оператор попадают специфичные пропсы (атрибуты) virtuoso
@@ -56,4 +57,27 @@ const Row = (props) => {
56
57
  // @ts-ignore
57
58
  renderRow: renderRow })] }));
58
59
  };
59
- exports.Row = Row;
60
+ const arePropsEqual = (prevProps, nextProps) => {
61
+ const deepEqualityProps = new Set(['row']);
62
+ // Список пропсов, которые нужно игнорировать при сравнении
63
+ const skipProps = new Set([
64
+ 'selectedRows',
65
+ 'onSelectRow',
66
+ 'columns',
67
+ ]);
68
+ const keys = Object.keys(prevProps);
69
+ return keys.every((key) => {
70
+ const typedKey = key;
71
+ // Не сравниваем свойства, которые нужно пропустить
72
+ if (skipProps.has(typedKey)) {
73
+ return true;
74
+ }
75
+ const prevValue = prevProps[typedKey];
76
+ const nextValue = nextProps[typedKey];
77
+ if (deepEqualityProps.has(typedKey)) {
78
+ return (0, external_1.checkIsDeepEqual)(prevValue, nextValue);
79
+ }
80
+ return prevValue === nextValue;
81
+ });
82
+ };
83
+ exports.Row = (0, react_1.memo)(RowComponent, arePropsEqual);
@@ -7,19 +7,20 @@ const RowContext_1 = require("../RowContext");
7
7
  const RowContextProvider = ({ children }) => {
8
8
  const [isDisabled, setDisabled] = (0, react_1.useState)(false);
9
9
  const [disabledReason, setDisabledReason] = (0, react_1.useState)();
10
- const addDisabledRow = (reason) => {
10
+ const addDisabledRow = (0, react_1.useCallback)((reason) => {
11
11
  setDisabled(true);
12
12
  setDisabledReason(reason);
13
- };
14
- const removeDisabledRow = () => {
13
+ }, []);
14
+ const removeDisabledRow = (0, react_1.useCallback)(() => {
15
15
  setDisabled(false);
16
16
  setDisabledReason(undefined);
17
- };
18
- return ((0, jsx_runtime_1.jsx)(RowContext_1.RowContext.Provider, { value: {
19
- isDisabled,
20
- disabledReason,
21
- addDisabledRow,
22
- removeDisabledRow,
23
- }, children: children }));
17
+ }, []);
18
+ const contextValue = (0, react_1.useMemo)(() => ({
19
+ isDisabled,
20
+ disabledReason,
21
+ addDisabledRow,
22
+ removeDisabledRow,
23
+ }), [isDisabled, disabledReason, addDisabledRow, removeDisabledRow]);
24
+ return ((0, jsx_runtime_1.jsx)(RowContext_1.RowContext.Provider, { value: contextValue, children: children }));
24
25
  };
25
26
  exports.RowContextProvider = RowContextProvider;
@@ -10,6 +10,11 @@ const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, selectedR
10
10
  const isFirstRender = (0, hooks_1.useFirstMountState)();
11
11
  const isSelectable = Boolean(onSelectRow);
12
12
  const isDataGridDisabled = isLoading || isDisabled;
13
+ // Реф добавлен для избежания замыкания selectedRows в handleSelectRow (иначе в handleSelectRow приходят некорректные значения selectedRows).
14
+ const selectedRowsRef = (0, react_1.useRef)(selectedRows);
15
+ (0, react_1.useEffect)(() => {
16
+ selectedRowsRef.current = selectedRows;
17
+ }, [selectedRows]);
13
18
  const treeRenderConfig = Object.is(variant, enums_1.Variant.Subrows)
14
19
  ? { ...subrows, isInitialExpanded: true }
15
20
  : tree;
@@ -43,15 +48,15 @@ const useLogic = ({ keyId, columns, rows = [], variant, tree, subrows, selectedR
43
48
  const filteredRows = selectedRows.filter((selectedRow) => !rows.find((row) => row[keyId] === selectedRow[keyId]));
44
49
  onSelectRow(filteredRows);
45
50
  };
46
- const handleSelectRow = (0, react_1.useCallback)((row) => (event) => {
51
+ const handleSelectRow = (row) => (event) => {
47
52
  if (!onSelectRow) {
48
53
  return;
49
54
  }
50
55
  if (event.target.checked) {
51
- return onSelectRow([...selectedRows, row]);
56
+ return onSelectRow([...selectedRowsRef.current, row]);
52
57
  }
53
- onSelectRow(selectedRows.filter((selectedRow) => selectedRow[keyId] !== row[keyId]));
54
- }, [selectedRows, onSelectRow, keyId]);
58
+ onSelectRow(selectedRowsRef.current.filter((selectedRow) => selectedRow[keyId] !== row[keyId]));
59
+ };
55
60
  return {
56
61
  isDataGridDisabled,
57
62
  treeRenderConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astral/ui",
3
- "version": "4.2.2",
3
+ "version": "4.2.3",
4
4
  "browser": "./index.js",
5
5
  "main": "./node/index.js",
6
6
  "dependencies": {