@astral/ui 4.7.0 → 4.9.0
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.
- package/components/CheckableTag/styles.js +3 -3
- package/components/NavMenu/Item/ItemButton/styles.js +4 -4
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/OptionsModal.js +7 -2
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/styles.js +1 -1
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.d.ts +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.js +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.d.ts +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.js +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.d.ts +20 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.js +67 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.d.ts +5 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.js +56 -5
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.d.ts +6 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.js +101 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.d.ts +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.js +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.d.ts +2 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.js +43 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.d.ts +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.js +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.d.ts +3 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.js +3 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.d.ts +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.js +1 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.d.ts +7 -0
- package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.js +31 -0
- package/components/TreeLikeAsyncAutocomplete/types.d.ts +50 -3
- package/components/TreeLikeAsyncAutocomplete/useLogic/useLogic.d.ts +4 -1
- package/node/components/CheckableTag/styles.js +3 -3
- package/node/components/NavMenu/Item/ItemButton/styles.js +4 -4
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/OptionsModal.js +7 -2
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/styles.js +1 -1
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.d.ts +1 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.js +17 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.d.ts +1 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.js +17 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.d.ts +20 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.js +71 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.d.ts +5 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.js +56 -5
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.d.ts +6 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.js +105 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.d.ts +1 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.js +17 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.d.ts +2 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.js +47 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.d.ts +1 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.js +17 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.d.ts +3 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.js +19 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.d.ts +1 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.js +17 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.d.ts +7 -0
- package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.js +35 -0
- package/node/components/TreeLikeAsyncAutocomplete/types.d.ts +50 -3
- package/node/components/TreeLikeAsyncAutocomplete/useLogic/useLogic.d.ts +4 -1
- package/package.json +1 -1
|
@@ -20,8 +20,8 @@ exports.StyledListItemButton = (0, styles_1.styled)(ListItemButton_1.ListItemBut
|
|
|
20
20
|
overflow: hidden;
|
|
21
21
|
|
|
22
22
|
box-sizing: border-box;
|
|
23
|
-
min-height:
|
|
24
|
-
padding: ${({ theme }) => theme.microSpacing(
|
|
23
|
+
min-height: 44px;
|
|
24
|
+
padding: ${({ theme }) => theme.microSpacing(6, 5)};
|
|
25
25
|
|
|
26
26
|
border-radius: ${({ theme }) => theme.shape.small};
|
|
27
27
|
|
|
@@ -68,8 +68,8 @@ exports.StyledListItemButton = (0, styles_1.styled)(ListItemButton_1.ListItemBut
|
|
|
68
68
|
${({ theme }) => theme.breakpoints.down('sm')} {
|
|
69
69
|
overflow: hidden;
|
|
70
70
|
|
|
71
|
-
min-height:
|
|
72
|
-
padding: ${({ theme }) => theme.spacing(
|
|
71
|
+
min-height: 48px;
|
|
72
|
+
padding: ${({ theme }) => theme.spacing(3, 2, 3, 4)};
|
|
73
73
|
|
|
74
74
|
border-radius: ${({ theme }) => theme.shape.small};
|
|
75
75
|
|
|
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.OptionsModal = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const Button_1 = require("../../Button");
|
|
6
|
+
const CheckableTag_1 = require("../../CheckableTag");
|
|
6
7
|
const Dialog_1 = require("../../Dialog");
|
|
7
8
|
const DialogActions_1 = require("../../DialogActions");
|
|
8
9
|
const Paper_1 = require("../../Paper");
|
|
10
|
+
const TagBadge_1 = require("../../TagBadge");
|
|
9
11
|
const TreeLikeList_1 = require("../../TreeLikeList");
|
|
10
12
|
const Typography_1 = require("../../Typography");
|
|
11
13
|
const constants_1 = require("./constants");
|
|
@@ -16,7 +18,7 @@ const SearchSuggestion_1 = require("./SearchSuggestion");
|
|
|
16
18
|
const styles_1 = require("./styles");
|
|
17
19
|
const useLogic_1 = require("./useLogic");
|
|
18
20
|
const OptionsModal = (props) => {
|
|
19
|
-
const { isNoResult, searchFieldProps, modalProps, treeListProps, cancelButtonProps, confirmButtonProps, handleRetry, isShowUserHint, isShowModalLoader, isShowSearchFieldLoader, isShowSearchSuggestion, isLoadingError, treeProps, loadingErrorMsg, } = (0, useLogic_1.useLogic)(props);
|
|
21
|
+
const { isNoResult, searchFieldProps, modalProps, treeListProps, cancelButtonProps, confirmButtonProps, handleRetry, isShowUserHint, isShowModalLoader, isShowSearchFieldLoader, isShowSearchSuggestion, isLoadingError, treeProps, loadingErrorMsg, badgeContent, checkableTagProps, isShowSelectedTree, selectedTreeProps, } = (0, useLogic_1.useLogic)(props);
|
|
20
22
|
const renderComponent = () => {
|
|
21
23
|
if (isShowSearchSuggestion) {
|
|
22
24
|
return (0, jsx_runtime_1.jsx)(SearchSuggestion_1.SearchSuggestion, {});
|
|
@@ -30,8 +32,11 @@ const OptionsModal = (props) => {
|
|
|
30
32
|
if (isNoResult) {
|
|
31
33
|
return (0, jsx_runtime_1.jsx)(NoData_1.NoData, {});
|
|
32
34
|
}
|
|
35
|
+
if (isShowSelectedTree) {
|
|
36
|
+
return ((0, jsx_runtime_1.jsx)(TreeLikeList_1.TreeLikeList, { ...treeProps, ...selectedTreeProps }));
|
|
37
|
+
}
|
|
33
38
|
return ((0, jsx_runtime_1.jsx)(TreeLikeList_1.TreeLikeList, { ...treeProps, ...treeListProps }));
|
|
34
39
|
};
|
|
35
|
-
return ((0, jsx_runtime_1.jsxs)(Dialog_1.Dialog, { ...modalProps, children: [(0, jsx_runtime_1.jsxs)(styles_1.StyledDialogContent, { "$size": modalProps.size, children: [(0, jsx_runtime_1.jsx)(styles_1.StyledSearchField, { fullWidth: true, ...searchFieldProps, isLoading: isShowSearchFieldLoader, helperText: constants_1.SEARCH_FIELD_HELPER_TEXT }), (0, jsx_runtime_1.jsx)(Paper_1.Paper, { variant: "outlined", children: (0, jsx_runtime_1.jsxs)(styles_1.TreeListWrapper, { children: [renderComponent(), isShowUserHint && ((0, jsx_runtime_1.jsx)(styles_1.UserHintWrapper, { children: (0, jsx_runtime_1.jsx)(Typography_1.Typography, { variant: "caption", color: "grey", colorIntensity: "600", children: "\u0423\u0442\u043E\u0447\u043D\u0438\u0442\u0435 \u0437\u0430\u043F\u0440\u043E\u0441 \u0434\u043B\u044F \u0431\u043E\u043B\u0435\u0435 \u043B\u0443\u0447\u0448\u0435\u0433\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0430 \u043F\u043E\u0438\u0441\u043A\u0430" }) }))] }) })] }), (0, jsx_runtime_1.jsxs)(DialogActions_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { variant: "text", ...cancelButtonProps, children: "\u041E\u0442\u043C\u0435\u043D\u0430" }), (0, jsx_runtime_1.jsx)(Button_1.Button, { ...confirmButtonProps, children: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C" })] })] }));
|
|
40
|
+
return ((0, jsx_runtime_1.jsxs)(Dialog_1.Dialog, { ...modalProps, children: [(0, jsx_runtime_1.jsxs)(styles_1.StyledDialogContent, { "$size": modalProps.size, children: [(0, jsx_runtime_1.jsx)(styles_1.StyledSearchField, { fullWidth: true, ...searchFieldProps, isLoading: isShowSearchFieldLoader, helperText: constants_1.SEARCH_FIELD_HELPER_TEXT }), (0, jsx_runtime_1.jsx)(CheckableTag_1.CheckableTag, { ...checkableTagProps, endAddon: (badgeProps) => ((0, jsx_runtime_1.jsx)(TagBadge_1.TagBadge, { ...badgeProps, badgeContent: badgeContent, showZero: true })) }), (0, jsx_runtime_1.jsx)(Paper_1.Paper, { variant: "outlined", children: (0, jsx_runtime_1.jsxs)(styles_1.TreeListWrapper, { children: [renderComponent(), isShowUserHint && ((0, jsx_runtime_1.jsx)(styles_1.UserHintWrapper, { children: (0, jsx_runtime_1.jsx)(Typography_1.Typography, { variant: "caption", color: "grey", colorIntensity: "600", children: "\u0423\u0442\u043E\u0447\u043D\u0438\u0442\u0435 \u0437\u0430\u043F\u0440\u043E\u0441 \u0434\u043B\u044F \u0431\u043E\u043B\u0435\u0435 \u043B\u0443\u0447\u0448\u0435\u0433\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0430 \u043F\u043E\u0438\u0441\u043A\u0430" }) }))] }) })] }), (0, jsx_runtime_1.jsxs)(DialogActions_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { variant: "text", ...cancelButtonProps, children: "\u041E\u0442\u043C\u0435\u043D\u0430" }), (0, jsx_runtime_1.jsx)(Button_1.Button, { ...confirmButtonProps, children: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C" })] })] }));
|
|
36
41
|
};
|
|
37
42
|
exports.OptionsModal = OptionsModal;
|
|
@@ -12,7 +12,7 @@ exports.StyledDialogContent = (0, styles_1.styled)(DialogContent_1.DialogContent
|
|
|
12
12
|
}) `
|
|
13
13
|
display: grid;
|
|
14
14
|
grid-template-columns: 100%;
|
|
15
|
-
grid-template-rows: max-content 1fr;
|
|
15
|
+
grid-template-rows: max-content 32px 1fr;
|
|
16
16
|
gap: ${({ theme }) => theme.spacing(4)};
|
|
17
17
|
|
|
18
18
|
width: ${({ $size }) => Dialog_1.DIALOG_SIZES[$size].minWidth};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useSelectedTreeView';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./useSelectedTreeView"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useSelectedTreeView';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./useSelectedTreeView"), exports);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ChangeEvent } from 'react';
|
|
2
|
+
import { type TreeListData } from '../../../../../Tree';
|
|
3
|
+
import type { TreeLikeAsyncAutocompleteValue } from '../../../../types';
|
|
4
|
+
type UseSelectedTreeViewParams = {
|
|
5
|
+
value?: TreeLikeAsyncAutocompleteValue;
|
|
6
|
+
searchValue?: string;
|
|
7
|
+
onInputChange: (value: string) => void;
|
|
8
|
+
onClearSearch: () => void;
|
|
9
|
+
};
|
|
10
|
+
type UseSelectedTreeViewResult = {
|
|
11
|
+
isShowSelectedTree: boolean;
|
|
12
|
+
setIsShowSelectedTree: (value: boolean) => void;
|
|
13
|
+
selectedTreeData: TreeLikeAsyncAutocompleteValue;
|
|
14
|
+
filteredSelectedTreeData: TreeListData[];
|
|
15
|
+
selectedTreeDataToRender: TreeListData[];
|
|
16
|
+
onChangeTreeView: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
17
|
+
isNoResultInSelectedTree: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare const useSelectedTreeView: ({ value, searchValue, onInputChange, onClearSearch, }: UseSelectedTreeViewParams) => UseSelectedTreeViewResult;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useSelectedTreeView = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const array_1 = require("../../../../../utils/array");
|
|
6
|
+
const utils_1 = require("../../utils");
|
|
7
|
+
const addIsForceExpandedFlag = (item) => {
|
|
8
|
+
if (item.children) {
|
|
9
|
+
return {
|
|
10
|
+
...item,
|
|
11
|
+
options: { ...(item.options || {}), isForceExpanded: true },
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return item;
|
|
15
|
+
};
|
|
16
|
+
const useSelectedTreeView = ({ value, searchValue, onInputChange, onClearSearch, }) => {
|
|
17
|
+
const [isShowSelectedTree, setIsShowSelectedTree] = (0, react_1.useState)();
|
|
18
|
+
// Сохраняем порядок узлов
|
|
19
|
+
const nodeOrderRef = (0, react_1.useRef)(new Map());
|
|
20
|
+
// Строим дерево из выбранных элементов
|
|
21
|
+
const selectedTreeData = (0, react_1.useMemo)(() => {
|
|
22
|
+
if (!value || value.length === 0) {
|
|
23
|
+
nodeOrderRef.current.clear();
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const tree = (0, utils_1.buildSelectedTree)(value);
|
|
27
|
+
return (0, utils_1.preserveNodeOrder)(tree, nodeOrderRef.current);
|
|
28
|
+
}, [value]);
|
|
29
|
+
// Применяем клиентский поиск к выбранным элементам
|
|
30
|
+
const filteredSelectedTreeData = (0, react_1.useMemo)(() => {
|
|
31
|
+
if (!isShowSelectedTree || !searchValue) {
|
|
32
|
+
return selectedTreeData;
|
|
33
|
+
}
|
|
34
|
+
return (0, utils_1.findInTree)(selectedTreeData, searchValue);
|
|
35
|
+
}, [selectedTreeData, searchValue, isShowSelectedTree]);
|
|
36
|
+
// Определяем isNoResult для режима выбранных элементов
|
|
37
|
+
const isNoResultInSelectedTree = !searchValue || !isShowSelectedTree
|
|
38
|
+
? false
|
|
39
|
+
: filteredSelectedTreeData.length === 0;
|
|
40
|
+
// Применяем флаг раскрытия для отфильтрованного дерева выбранных элементов
|
|
41
|
+
const selectedTreeDataToRender = (0, react_1.useMemo)(() => {
|
|
42
|
+
if (!searchValue || !isShowSelectedTree) {
|
|
43
|
+
return filteredSelectedTreeData;
|
|
44
|
+
}
|
|
45
|
+
return (0, array_1.deepMap)(filteredSelectedTreeData, 'children', addIsForceExpandedFlag);
|
|
46
|
+
}, [filteredSelectedTreeData, searchValue, isShowSelectedTree]);
|
|
47
|
+
const onChangeTreeView = (event) => {
|
|
48
|
+
const newIsShowSelectedTree = event.target.checked;
|
|
49
|
+
setIsShowSelectedTree(newIsShowSelectedTree);
|
|
50
|
+
onClearSearch();
|
|
51
|
+
if (!newIsShowSelectedTree) {
|
|
52
|
+
onInputChange('');
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
(0, react_1.useEffect)(() => {
|
|
56
|
+
// Когда все элементы убраны из выбранного дерева, возвращаемся на основное дерево
|
|
57
|
+
if (!value?.length) {
|
|
58
|
+
setIsShowSelectedTree(false);
|
|
59
|
+
}
|
|
60
|
+
}, [value]);
|
|
61
|
+
return {
|
|
62
|
+
isShowSelectedTree: isShowSelectedTree ?? false,
|
|
63
|
+
setIsShowSelectedTree,
|
|
64
|
+
selectedTreeData,
|
|
65
|
+
filteredSelectedTreeData,
|
|
66
|
+
selectedTreeDataToRender,
|
|
67
|
+
onChangeTreeView,
|
|
68
|
+
isNoResultInSelectedTree,
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
exports.useSelectedTreeView = useSelectedTreeView;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ButtonProps } from '../../../Button';
|
|
2
|
+
import { type CheckableTagProps } from '../../../CheckableTag';
|
|
2
3
|
import { type DialogProps, type DialogSize } from '../../../Dialog';
|
|
3
4
|
import { type SearchFieldProps } from '../../../SearchField';
|
|
4
5
|
import { type TreeLikeListProps } from '../../../TreeLikeList';
|
|
@@ -22,6 +23,10 @@ type UseLogicResult = {
|
|
|
22
23
|
treeProps?: Pick<TreeLikeListProps, 'disabledItems' | 'renderItem'>;
|
|
23
24
|
isLoadingError?: boolean;
|
|
24
25
|
loadingErrorMsg?: string;
|
|
26
|
+
badgeContent: number;
|
|
27
|
+
selectedTreeProps: TreeLikeListProps<TreeLikeAsyncAutocompleteValue>;
|
|
28
|
+
isShowSelectedTree?: boolean;
|
|
29
|
+
checkableTagProps: CheckableTagProps;
|
|
25
30
|
};
|
|
26
31
|
export declare const useLogic: ({ isOpen, initialValue, options, onChange, dialogProps, onInputChange, onRetry, meta, onShowUserHint, isLoadingModal, minSymbolsToFetch, isLoading, isLoadingError, loadingErrorMsg, treeProps, }: UseLogicParams) => UseLogicResult;
|
|
27
32
|
export {};
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.useLogic = void 0;
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const array_1 = require("../../../utils/array");
|
|
6
|
+
const hooks_1 = require("./hooks");
|
|
6
7
|
const useLogic = ({ isOpen, initialValue, options, onChange, dialogProps, onInputChange, onRetry, meta, onShowUserHint, isLoadingModal, minSymbolsToFetch = 0, isLoading, isLoadingError, loadingErrorMsg, treeProps, }) => {
|
|
7
8
|
const [value, setValue] = (0, react_1.useState)(initialValue);
|
|
8
9
|
const [searchValue, setSearchValue] = (0, react_1.useState)();
|
|
@@ -16,6 +17,15 @@ const useLogic = ({ isOpen, initialValue, options, onChange, dialogProps, onInpu
|
|
|
16
17
|
setSearchValue('');
|
|
17
18
|
}
|
|
18
19
|
}, [isOpen]);
|
|
20
|
+
const onClearSearch = () => {
|
|
21
|
+
setSearchValue('');
|
|
22
|
+
};
|
|
23
|
+
const { isShowSelectedTree, selectedTreeDataToRender, onChangeTreeView, isNoResultInSelectedTree, } = (0, hooks_1.useSelectedTreeView)({
|
|
24
|
+
value,
|
|
25
|
+
searchValue,
|
|
26
|
+
onInputChange,
|
|
27
|
+
onClearSearch,
|
|
28
|
+
});
|
|
19
29
|
const addIsForceExpandedFlag = (item) => {
|
|
20
30
|
if (item.children) {
|
|
21
31
|
return {
|
|
@@ -30,8 +40,13 @@ const useLogic = ({ isOpen, initialValue, options, onChange, dialogProps, onInpu
|
|
|
30
40
|
? (0, array_1.deepMap)(options, 'children', addIsForceExpandedFlag)
|
|
31
41
|
: options, [options, searchValue, isLoading]);
|
|
32
42
|
const handleChangeSearchField = (event) => {
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
const newSearchValue = event.target.value;
|
|
44
|
+
setSearchValue(newSearchValue);
|
|
45
|
+
// В режиме выбранных элементов используем клиентский поиск, не вызываем onInputChange
|
|
46
|
+
// В обычном режиме используем асинхронный поиск через запросы
|
|
47
|
+
if (!isShowSelectedTree) {
|
|
48
|
+
onInputChange(newSearchValue);
|
|
49
|
+
}
|
|
35
50
|
};
|
|
36
51
|
const handleChange = (newValue) => setValue(newValue);
|
|
37
52
|
const handleClose = (event) => {
|
|
@@ -46,7 +61,23 @@ const useLogic = ({ isOpen, initialValue, options, onChange, dialogProps, onInpu
|
|
|
46
61
|
onChange?.(value);
|
|
47
62
|
onClose?.(event, 'escapeKeyDown');
|
|
48
63
|
};
|
|
49
|
-
|
|
64
|
+
// Определяем isNoResult в зависимости от режима
|
|
65
|
+
const isNoResult = (0, react_1.useMemo)(() => {
|
|
66
|
+
if (!searchValue) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
// В режиме выбранных элементов проверяем отфильтрованное дерево
|
|
70
|
+
if (isShowSelectedTree) {
|
|
71
|
+
return isNoResultInSelectedTree;
|
|
72
|
+
}
|
|
73
|
+
// В обычном режиме проверяем options
|
|
74
|
+
return !options.length;
|
|
75
|
+
}, [
|
|
76
|
+
searchValue,
|
|
77
|
+
isShowSelectedTree,
|
|
78
|
+
isNoResultInSelectedTree,
|
|
79
|
+
options.length,
|
|
80
|
+
]);
|
|
50
81
|
const isDisabledButton = !value || isNoResult;
|
|
51
82
|
const handleRetry = () => {
|
|
52
83
|
onRetry?.(searchValue);
|
|
@@ -55,8 +86,10 @@ const useLogic = ({ isOpen, initialValue, options, onChange, dialogProps, onInpu
|
|
|
55
86
|
const isShowModalLoader = isLoadingModal && options.length === 0;
|
|
56
87
|
const isShowSearchFieldLoader = (isLoading && !isLoadingModal) ||
|
|
57
88
|
(isLoading && isLoadingModal && options.length > 0);
|
|
58
|
-
|
|
59
|
-
|
|
89
|
+
// Подсказка о минимальном количестве символов показывается только в обычном режиме
|
|
90
|
+
const isShowSearchSuggestion = Boolean(!isShowSelectedTree &&
|
|
91
|
+
((!searchValue && minSymbolsToFetch > 0) ||
|
|
92
|
+
(searchValue && searchValue.length < minSymbolsToFetch)));
|
|
60
93
|
return {
|
|
61
94
|
isShowModalLoader,
|
|
62
95
|
isShowSearchFieldLoader,
|
|
@@ -67,6 +100,24 @@ const useLogic = ({ isOpen, initialValue, options, onChange, dialogProps, onInpu
|
|
|
67
100
|
treeProps,
|
|
68
101
|
loadingErrorMsg,
|
|
69
102
|
isLoadingError,
|
|
103
|
+
badgeContent: value?.length ?? 0,
|
|
104
|
+
isShowSelectedTree,
|
|
105
|
+
checkableTagProps: {
|
|
106
|
+
onChange: onChangeTreeView,
|
|
107
|
+
checked: isShowSelectedTree,
|
|
108
|
+
disabled: !value?.length,
|
|
109
|
+
size: 'large',
|
|
110
|
+
variant: 'light',
|
|
111
|
+
label: 'Показать выбранные',
|
|
112
|
+
color: 'default',
|
|
113
|
+
},
|
|
114
|
+
selectedTreeProps: {
|
|
115
|
+
value,
|
|
116
|
+
data: selectedTreeDataToRender,
|
|
117
|
+
onChange: handleChange,
|
|
118
|
+
isObjectMode: true,
|
|
119
|
+
isInitialExpanded: true,
|
|
120
|
+
},
|
|
70
121
|
modalProps: {
|
|
71
122
|
onClose: handleClose,
|
|
72
123
|
disableRestoreFocus: true,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TreeLikeAsyncAutocompleteValue } from '../../../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Строит дерево из выбранных элементов с учетом breadcrumbs
|
|
4
|
+
* Если несколько элементов имеют общего родителя, они объединяются в один узел
|
|
5
|
+
*/
|
|
6
|
+
export declare const buildSelectedTree: (selectedItems: TreeLikeAsyncAutocompleteValue) => TreeLikeAsyncAutocompleteValue;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildSelectedTree = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Создает breadcrumbs для элемента на основе его позиции в пути
|
|
6
|
+
*/
|
|
7
|
+
const createBreadcrumbs = (path, currentIndex) => {
|
|
8
|
+
if (currentIndex === 0) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
// Берем все элементы до текущего индекса и создаем breadcrumbs
|
|
12
|
+
// Breadcrumbs должны содержать только id, label, note (без children и options)
|
|
13
|
+
return path.slice(0, currentIndex).map((item) => ({
|
|
14
|
+
id: item.id,
|
|
15
|
+
label: item.label,
|
|
16
|
+
note: item.note,
|
|
17
|
+
}));
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Получает или создает Map для дочернего узла
|
|
21
|
+
*/
|
|
22
|
+
const getOrCreateChildrenMap = (node, nodeChildrenMaps) => {
|
|
23
|
+
const existingMap = nodeChildrenMaps.get(node);
|
|
24
|
+
if (existingMap) {
|
|
25
|
+
return existingMap;
|
|
26
|
+
}
|
|
27
|
+
const childrenMap = new Map();
|
|
28
|
+
nodeChildrenMaps.set(node, childrenMap);
|
|
29
|
+
// Если у узла уже есть children, добавляем их в Map
|
|
30
|
+
if (node.children) {
|
|
31
|
+
node.children.forEach((child) => {
|
|
32
|
+
childrenMap.set(child.id, child);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return childrenMap;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Преобразует Map в массив TreeLikeAsyncAutocompleteValue
|
|
39
|
+
*/
|
|
40
|
+
const convertToArray = (level, nodeChildrenMaps) => {
|
|
41
|
+
const result = [];
|
|
42
|
+
level.forEach((node) => {
|
|
43
|
+
const childrenMap = nodeChildrenMaps.get(node);
|
|
44
|
+
const children = childrenMap && childrenMap.size > 0
|
|
45
|
+
? convertToArray(childrenMap, nodeChildrenMaps)
|
|
46
|
+
: undefined;
|
|
47
|
+
result.push({
|
|
48
|
+
...node,
|
|
49
|
+
children: children && children.length > 0 ? children : undefined,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Строит дерево из выбранных элементов с учетом breadcrumbs
|
|
56
|
+
* Если несколько элементов имеют общего родителя, они объединяются в один узел
|
|
57
|
+
*/
|
|
58
|
+
const buildSelectedTree = (selectedItems) => {
|
|
59
|
+
if (!selectedItems || selectedItems.length === 0) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const rootLevel = new Map();
|
|
63
|
+
const nodeChildrenMaps = new WeakMap();
|
|
64
|
+
// Обрабатываем каждый выбранный элемент
|
|
65
|
+
selectedItems.forEach((item) => {
|
|
66
|
+
// Строим путь от корня к элементу: breadcrumbs + сам элемент
|
|
67
|
+
const path = [
|
|
68
|
+
...(item.breadcrumbs ?? []),
|
|
69
|
+
item,
|
|
70
|
+
];
|
|
71
|
+
// Проходим по пути и создаем/обновляем узлы дерева
|
|
72
|
+
path.reduce((currentLevel, pathItem, index) => {
|
|
73
|
+
const existingNode = currentLevel.get(pathItem.id);
|
|
74
|
+
const restoredBreadcrumbs = createBreadcrumbs(path, index);
|
|
75
|
+
if (existingNode) {
|
|
76
|
+
// Если узел уже существует, обновляем его поля из pathItem
|
|
77
|
+
const existingChildren = existingNode.children;
|
|
78
|
+
Object.assign(existingNode, pathItem, {
|
|
79
|
+
children: existingChildren,
|
|
80
|
+
breadcrumbs: restoredBreadcrumbs,
|
|
81
|
+
options: {
|
|
82
|
+
...existingNode.options,
|
|
83
|
+
...pathItem.options,
|
|
84
|
+
isDefaultExpanded: true,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
return getOrCreateChildrenMap(existingNode, nodeChildrenMaps);
|
|
88
|
+
}
|
|
89
|
+
// Создаем новый узел с восстановленными breadcrumbs
|
|
90
|
+
const newNode = {
|
|
91
|
+
...pathItem,
|
|
92
|
+
breadcrumbs: restoredBreadcrumbs,
|
|
93
|
+
children: undefined,
|
|
94
|
+
options: {
|
|
95
|
+
...pathItem.options,
|
|
96
|
+
isDefaultExpanded: true,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
currentLevel.set(pathItem.id, newNode);
|
|
100
|
+
return getOrCreateChildrenMap(newNode, nodeChildrenMaps);
|
|
101
|
+
}, rootLevel);
|
|
102
|
+
});
|
|
103
|
+
return convertToArray(rootLevel, nodeChildrenMaps);
|
|
104
|
+
};
|
|
105
|
+
exports.buildSelectedTree = buildSelectedTree;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './buildSelectedTree';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./buildSelectedTree"), exports);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findInTree = void 0;
|
|
4
|
+
const defaultFilterOptions = (node, searchValue) => {
|
|
5
|
+
if (typeof node.label !== 'string') {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const preparedSearchValue = searchValue.trim().toLowerCase();
|
|
9
|
+
const preparedLabel = node.label.toLowerCase();
|
|
10
|
+
const resultByLabel = preparedLabel.includes(preparedSearchValue);
|
|
11
|
+
if (resultByLabel) {
|
|
12
|
+
return resultByLabel;
|
|
13
|
+
}
|
|
14
|
+
if (typeof node.note !== 'string') {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const preparedNote = node.note.toLowerCase();
|
|
18
|
+
return preparedNote.includes(preparedSearchValue);
|
|
19
|
+
};
|
|
20
|
+
const findInTree = (tree, searchValue, filterOptions) => {
|
|
21
|
+
const compareFunc = filterOptions || defaultFilterOptions;
|
|
22
|
+
const search = (nodes) => {
|
|
23
|
+
const results = [];
|
|
24
|
+
for (const node of nodes) {
|
|
25
|
+
// Если label включает искомый термин, добавляем узел в результаты
|
|
26
|
+
if (compareFunc(node, searchValue)) {
|
|
27
|
+
results.push({ ...node, options: { isDefaultExpanded: false } });
|
|
28
|
+
// Если узел соответствует поиску, пропускаем обработку его потомков
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// Если у узла есть потомки, рекурсивно ищем в них
|
|
32
|
+
if (node.children) {
|
|
33
|
+
const childResults = search(node.children);
|
|
34
|
+
if (childResults.length > 0) {
|
|
35
|
+
// Добавляем узел только один раз с найденными потомками
|
|
36
|
+
results.push({
|
|
37
|
+
...node,
|
|
38
|
+
children: childResults,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
};
|
|
45
|
+
return search(tree);
|
|
46
|
+
};
|
|
47
|
+
exports.findInTree = findInTree;
|
package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './findInTree';
|
package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./findInTree"), exports);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./buildSelectedTree"), exports);
|
|
18
|
+
__exportStar(require("./findInTree"), exports);
|
|
19
|
+
__exportStar(require("./preserveNodeOrder"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './preserveNodeOrder';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./preserveNodeOrder"), exports);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TreeLikeAsyncAutocompleteValue } from '../../../../types';
|
|
2
|
+
type NodeOrderMap = Map<string, number>;
|
|
3
|
+
/**
|
|
4
|
+
* Гарантирует неизменность порядка узлов при операциях выбора элементов в дереве
|
|
5
|
+
*/
|
|
6
|
+
export declare const preserveNodeOrder: (tree: TreeLikeAsyncAutocompleteValue, nodeOrderRef: NodeOrderMap) => TreeLikeAsyncAutocompleteValue;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.preserveNodeOrder = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Гарантирует неизменность порядка узлов при операциях выбора элементов в дереве
|
|
6
|
+
*/
|
|
7
|
+
const preserveNodeOrder = (tree, nodeOrderRef) => {
|
|
8
|
+
// Обновляем порядок узлов: сохраняем существующий или используем порядок из дерева
|
|
9
|
+
const updateNodeOrder = (nodes) => {
|
|
10
|
+
nodes.forEach((node) => {
|
|
11
|
+
const nodeWithOrder = node;
|
|
12
|
+
const orderFromTree = nodeWithOrder.options?.order;
|
|
13
|
+
if (!nodeOrderRef.has(node.id)) {
|
|
14
|
+
nodeOrderRef.set(node.id, orderFromTree ?? nodeOrderRef.size);
|
|
15
|
+
}
|
|
16
|
+
if (node.children) {
|
|
17
|
+
updateNodeOrder(node.children);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
updateNodeOrder(tree);
|
|
22
|
+
// Сортируем дерево по сохраненному порядку
|
|
23
|
+
const sortByOrder = (nodes) => [...nodes]
|
|
24
|
+
.sort((a, b) => {
|
|
25
|
+
const orderA = nodeOrderRef.get(a.id) ?? Infinity;
|
|
26
|
+
const orderB = nodeOrderRef.get(b.id) ?? Infinity;
|
|
27
|
+
return orderA - orderB;
|
|
28
|
+
})
|
|
29
|
+
.map((node) => ({
|
|
30
|
+
...node,
|
|
31
|
+
children: node.children ? sortByOrder(node.children) : undefined,
|
|
32
|
+
}));
|
|
33
|
+
return sortByOrder(tree);
|
|
34
|
+
};
|
|
35
|
+
exports.preserveNodeOrder = preserveNodeOrder;
|