@astral/ui 4.6.0 → 4.8.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.
Files changed (65) hide show
  1. package/components/CheckableTag/styles.js +3 -3
  2. package/components/MenuOrganization/OrganizationButton/OrganizationButton.d.ts +7 -1
  3. package/components/MenuOrganization/OrganizationButton/OrganizationButton.js +4 -2
  4. package/components/MenuOrganization/types.d.ts +13 -0
  5. package/components/MenuOrganization/useLogic/useLogic.d.ts +4 -1
  6. package/components/MenuOrganization/useLogic/useLogic.js +5 -1
  7. package/components/TreeLikeAsyncAutocomplete/OptionsModal/OptionsModal.js +7 -2
  8. package/components/TreeLikeAsyncAutocomplete/OptionsModal/styles.js +1 -1
  9. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.d.ts +1 -0
  10. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.js +1 -0
  11. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.d.ts +1 -0
  12. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.js +1 -0
  13. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.d.ts +20 -0
  14. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.js +67 -0
  15. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.d.ts +5 -0
  16. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.js +56 -5
  17. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.d.ts +6 -0
  18. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.js +101 -0
  19. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.d.ts +1 -0
  20. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.js +1 -0
  21. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.d.ts +2 -0
  22. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.js +43 -0
  23. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.d.ts +1 -0
  24. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.js +1 -0
  25. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.d.ts +3 -0
  26. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.js +3 -0
  27. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.d.ts +1 -0
  28. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.js +1 -0
  29. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.d.ts +7 -0
  30. package/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.js +31 -0
  31. package/components/TreeLikeAsyncAutocomplete/types.d.ts +50 -3
  32. package/components/TreeLikeAsyncAutocomplete/useLogic/useLogic.d.ts +4 -1
  33. package/node/components/CheckableTag/styles.js +3 -3
  34. package/node/components/MenuOrganization/OrganizationButton/OrganizationButton.d.ts +7 -1
  35. package/node/components/MenuOrganization/OrganizationButton/OrganizationButton.js +4 -2
  36. package/node/components/MenuOrganization/types.d.ts +13 -0
  37. package/node/components/MenuOrganization/useLogic/useLogic.d.ts +4 -1
  38. package/node/components/MenuOrganization/useLogic/useLogic.js +5 -1
  39. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/OptionsModal.js +7 -2
  40. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/styles.js +1 -1
  41. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.d.ts +1 -0
  42. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/index.js +17 -0
  43. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.d.ts +1 -0
  44. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/index.js +17 -0
  45. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.d.ts +20 -0
  46. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/hooks/useSelectedTreeView/useSelectedTreeView.js +71 -0
  47. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.d.ts +5 -0
  48. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/useLogic.js +56 -5
  49. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.d.ts +6 -0
  50. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/buildSelectedTree.js +105 -0
  51. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.d.ts +1 -0
  52. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/buildSelectedTree/index.js +17 -0
  53. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.d.ts +2 -0
  54. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/findInTree.js +47 -0
  55. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.d.ts +1 -0
  56. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/findInTree/index.js +17 -0
  57. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.d.ts +3 -0
  58. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/index.js +19 -0
  59. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.d.ts +1 -0
  60. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/index.js +17 -0
  61. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.d.ts +7 -0
  62. package/node/components/TreeLikeAsyncAutocomplete/OptionsModal/useLogic/utils/preserveNodeOrder/preserveNodeOrder.js +35 -0
  63. package/node/components/TreeLikeAsyncAutocomplete/types.d.ts +50 -3
  64. package/node/components/TreeLikeAsyncAutocomplete/useLogic/useLogic.d.ts +4 -1
  65. package/package.json +1 -1
@@ -1,10 +1,56 @@
1
- import type { FetchOptionsResult, TreeAsyncAutocompleteValue } from '../TreeAsyncAutocomplete';
1
+ import type { TreeAsyncAutocompleteValue } from '../TreeAsyncAutocomplete';
2
2
  import type { TreeAutocompleteProps } from '../TreeAutocomplete';
3
+ type TreeBreadcrumbs = Omit<TreeAsyncAutocompleteValue, 'children' | 'options'>[];
4
+ type TreeLikeAsyncAutocompleteItem = Omit<TreeAsyncAutocompleteValue, 'children'> & {
5
+ /**
6
+ * Путь от корня дерева к текущему элементу в виде цепочки родительских узлов.
7
+ *
8
+ * Используется для отображения иерархической структуры выбранных элементов.
9
+ * Текущий элемент не включается в breadcrumbs.
10
+ *
11
+ * Структура breadcrumbs:
12
+ * - Первый элемент массива - корневой элемент дерева
13
+ * - Последний элемент массива - непосредственный родитель текущего элемента
14
+ *
15
+ * @example
16
+ * {
17
+ * id: '511',
18
+ * label: 'Отдел бухгалтерии',
19
+ * breadcrumbs: [
20
+ * { id: '5', label: 'ЗАО "ТехноИнновации"' }, // Первый элемент - корневой
21
+ * { id: '51', label: 'Департамент финансов' } // Последний элемент - родитель
22
+ * ]
23
+ * }
24
+ *
25
+ */
26
+ breadcrumbs?: TreeBreadcrumbs;
27
+ /**
28
+ * Дочерние узлы дерева
29
+ */
30
+ children?: TreeLikeAsyncAutocompleteItem[];
31
+ };
3
32
  /**
4
33
  * Значение для TreeLikeAsyncAutocomplete - массив элементов дерева
5
34
  */
6
- export type TreeLikeAsyncAutocompleteValue = TreeAsyncAutocompleteValue[];
7
- export type { FetchOptionsResult };
35
+ export type TreeLikeAsyncAutocompleteValue = TreeLikeAsyncAutocompleteItem[];
36
+ /**
37
+ * Результат функции загрузки опций
38
+ */
39
+ export type FetchOptionsResult = {
40
+ /**
41
+ * Элементы дерева
42
+ */
43
+ options: TreeLikeAsyncAutocompleteValue;
44
+ /**
45
+ * Метаданные результата загрузки
46
+ */
47
+ meta?: {
48
+ /**
49
+ * Флаг, указывающий, что все данные загружены
50
+ */
51
+ isAllDataLoaded: boolean;
52
+ };
53
+ };
8
54
  export type TreeLikeAsyncAutocompleteProps = Omit<TreeAutocompleteProps, 'options' | 'isLoading' | 'isLoadingError' | 'value' | 'onChange' | 'filterOptions' | 'trimmed' | 'maxLength' | 'type'> & {
9
55
  /**
10
56
  * Выбранное значения
@@ -67,3 +113,4 @@ export type TreeLikeAsyncAutocompleteProps = Omit<TreeAutocompleteProps, 'option
67
113
  */
68
114
  isLoading?: boolean;
69
115
  };
116
+ export {};
@@ -614,7 +614,10 @@ export declare const useLogic: ({ onBlur, isError, value, isDisabled, fetchDelay
614
614
  fullScreen?: boolean | undefined;
615
615
  disableBackdropClick?: boolean | undefined;
616
616
  };
617
- options: import("../../TreeAsyncAutocomplete").TreeAsyncAutocompleteValue[];
617
+ options: (Omit<import("../../TreeAsyncAutocomplete").TreeAsyncAutocompleteValue, "children"> & {
618
+ breadcrumbs?: Omit<import("../../TreeAsyncAutocomplete").TreeAsyncAutocompleteValue, "children" | "options">[] | undefined;
619
+ children?: (Omit<import("../../TreeAsyncAutocomplete").TreeAsyncAutocompleteValue, "children"> & any)[] | undefined;
620
+ })[];
618
621
  onRetry: (search?: string) => void;
619
622
  onChange: ((newValue?: import("../types").TreeLikeAsyncAutocompleteValue | undefined) => void) | undefined;
620
623
  onInputChange: (search: string) => void;
@@ -79,11 +79,11 @@ const getColor = ({ theme, isChecked, disabled, }) => {
79
79
  }
80
80
  return 'inherit';
81
81
  };
82
- const getBorder = ({ isChecked, variant, color, theme, tagState = 'default', }) => {
82
+ const getBorder = ({ isChecked, variant, color, theme, disabled, tagState = 'default', }) => {
83
83
  if (variant === 'text' || color !== 'default') {
84
84
  return 'none';
85
85
  }
86
- if (isChecked) {
86
+ if (isChecked || (disabled && variant === 'light')) {
87
87
  return '1px solid transparent';
88
88
  }
89
89
  if (tagState === 'default') {
@@ -107,7 +107,7 @@ exports.StyledTag = (0, styles_1.styled)(Tag_1.Tag, {
107
107
  `
108
108
  background-color: ${disabled ? theme.palette.grey[100] : theme.palette.grey[900]};
109
109
  `}
110
- border: ${({ theme, variant, $isChecked, color }) => getBorder({ theme, variant, isChecked: $isChecked, color })};
110
+ border: ${({ theme, variant, $isChecked, color, disabled }) => getBorder({ theme, variant, isChecked: $isChecked, color, disabled })};
111
111
 
112
112
  .${Tag_1.tagClassnames.label} {
113
113
  color: ${({ theme, disabled, $isChecked }) => getColor({ theme, isChecked: $isChecked, disabled })};
@@ -1,4 +1,4 @@
1
- import { type SyntheticEvent } from 'react';
1
+ import { type ReactNode, type SyntheticEvent } from 'react';
2
2
  import { type Organization } from '../types';
3
3
  export type OrganizationButtonProps = {
4
4
  onClick: (event: SyntheticEvent) => void;
@@ -10,5 +10,11 @@ export type OrganizationButtonProps = {
10
10
  * Скрытие персональных данных от инструментов мониторинга
11
11
  */
12
12
  isHidePersonalData?: boolean;
13
+ /**
14
+ * Render-props, позволяет кастомизировать содержимое кнопки
15
+ */
16
+ renderPreview?: (organization: Organization & Record<string, unknown>, params: {
17
+ className?: string;
18
+ }) => ReactNode;
13
19
  };
14
20
  export declare const OrganizationButton: import("react").ForwardRefExoticComponent<OrganizationButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -8,12 +8,14 @@ const personalDataSecurity_1 = require("../../personalDataSecurity");
8
8
  const Tooltip_1 = require("../../Tooltip");
9
9
  const styles_1 = require("../styles");
10
10
  const styles_2 = require("./styles");
11
- exports.OrganizationButton = (0, react_1.forwardRef)(({ onClick, isOpen, currentOrganization, isDisabled, disabledReason, isHidePersonalData, }, ref) => {
11
+ exports.OrganizationButton = (0, react_1.forwardRef)(({ onClick, isOpen, currentOrganization, isDisabled, disabledReason, isHidePersonalData, renderPreview, }, ref) => {
12
12
  const { name, inn, kpp } = currentOrganization;
13
13
  const hidePersonalDataClassname = (0, personalDataSecurity_1.useHidePersonalData)({
14
14
  isEnabled: isHidePersonalData,
15
15
  });
16
- const renderButton = () => ((0, jsx_runtime_1.jsx)(styles_2.StyledButton, { ref: ref, variant: "text", disabled: isDisabled, onClick: onClick, endIcon: (0, jsx_runtime_1.jsx)(styles_2.StyledChevron, { isActive: isOpen, width: 24, height: 24 }), children: (0, jsx_runtime_1.jsxs)(styles_2.Container, { className: hidePersonalDataClassname, children: [(0, jsx_runtime_1.jsx)(OverflowTypography_1.OverflowTypography, { variant: "h6", component: "div", children: name }), (0, jsx_runtime_1.jsxs)(styles_1.OrganizationData, { children: [(0, jsx_runtime_1.jsxs)(styles_2.StyledTypography, { "$isDisabled": isDisabled, variant: "caption", color: "textSecondary", children: ["\u0418\u041D\u041D ", inn] }), kpp && ((0, jsx_runtime_1.jsxs)(styles_2.StyledTypography, { "$isDisabled": isDisabled, variant: "caption", color: "textSecondary", children: ["\u041A\u041F\u041F ", kpp] }))] })] }) }));
16
+ const renderButton = () => ((0, jsx_runtime_1.jsx)(styles_2.StyledButton, { ref: ref, variant: "text", disabled: isDisabled, onClick: onClick, endIcon: (0, jsx_runtime_1.jsx)(styles_2.StyledChevron, { isActive: isOpen, width: 24, height: 24 }), children: renderPreview ? (renderPreview(currentOrganization, {
17
+ className: hidePersonalDataClassname,
18
+ })) : ((0, jsx_runtime_1.jsxs)(styles_2.Container, { className: hidePersonalDataClassname, children: [(0, jsx_runtime_1.jsx)(OverflowTypography_1.OverflowTypography, { variant: "h6", component: "div", children: name }), (0, jsx_runtime_1.jsxs)(styles_1.OrganizationData, { children: [(0, jsx_runtime_1.jsxs)(styles_2.StyledTypography, { "$isDisabled": isDisabled, variant: "caption", color: "textSecondary", children: ["\u0418\u041D\u041D ", inn] }), kpp && ((0, jsx_runtime_1.jsxs)(styles_2.StyledTypography, { "$isDisabled": isDisabled, variant: "caption", color: "textSecondary", children: ["\u041A\u041F\u041F ", kpp] }))] })] })) }));
17
19
  if (isDisabled && disabledReason) {
18
20
  return ((0, jsx_runtime_1.jsx)(Tooltip_1.Tooltip, { title: disabledReason, withoutContainer: false, placement: "bottom", children: renderButton() }));
19
21
  }
@@ -113,4 +113,17 @@ export type MenuOrganizationProps<TData extends Organization = Organization> = {
113
113
  * )}
114
114
  */
115
115
  renderItem?: (params: RenderItemParams<TData>) => ReactNode;
116
+ /**
117
+ * Render-props, позволяет кастомизировать содержимое кнопки OrganizationButton
118
+ *
119
+ * @example
120
+ * renderPreview={(organization) => (
121
+ * <Avatar color="#EC4899" size="md">
122
+ * {getInitials(organization.name)}
123
+ * </Avatar>
124
+ * )}
125
+ */
126
+ renderPreview?: (organization: TData, params: {
127
+ className?: string;
128
+ }) => ReactNode;
116
129
  };
@@ -1,7 +1,7 @@
1
1
  import { type ChangeEvent, type SyntheticEvent } from 'react';
2
2
  import { type MenuOrganizationProps, type Organization } from '../types';
3
3
  type UseLogicParams<TData extends Organization & Record<string, unknown>> = MenuOrganizationProps<TData>;
4
- export declare const useLogic: <TData extends Organization & Record<string, unknown>>({ organizations, onChangeSearch, onChange, currentOrganizationGroupLabel, currentOrganization, groupBy, onClose, onOpen, isOpen: isOpenPopover, isDisabled, disabledReason, isHidePersonalData, action, isLoading, isError, onRetry, renderItem, }: UseLogicParams<TData>) => {
4
+ export declare const useLogic: <TData extends Organization & Record<string, unknown>>({ organizations, onChangeSearch, onChange, currentOrganizationGroupLabel, currentOrganization, groupBy, onClose, onOpen, isOpen: isOpenPopover, isDisabled, disabledReason, isHidePersonalData, action, isLoading, isError, onRetry, renderItem, renderPreview, }: UseLogicParams<TData>) => {
5
5
  action: ({
6
6
  text: string;
7
7
  } & Pick<import("../..").ButtonProps, "onClick" | "href" | "endIcon" | "startIcon" | "variant" | "component">) | undefined;
@@ -19,6 +19,9 @@ export declare const useLogic: <TData extends Organization & Record<string, unkn
19
19
  isDisabled: boolean | undefined;
20
20
  disabledReason: string | undefined;
21
21
  isHidePersonalData: boolean | undefined;
22
+ renderPreview: ((organization: Organization & Record<string, unknown>, params: {
23
+ className?: string;
24
+ }) => import("react").ReactNode) | undefined;
22
25
  ref: (node: HTMLButtonElement | null) => void;
23
26
  };
24
27
  searchProps: {
@@ -5,7 +5,7 @@ const react_1 = require("react");
5
5
  const DashboardLayout_1 = require("../../DashboardLayout");
6
6
  const hooks_1 = require("../../hooks");
7
7
  const personalDataSecurity_1 = require("../../personalDataSecurity");
8
- const useLogic = ({ organizations = [], onChangeSearch, onChange, currentOrganizationGroupLabel, currentOrganization, groupBy, onClose, onOpen, isOpen: isOpenPopover, isDisabled, disabledReason, isHidePersonalData, action, isLoading, isError, onRetry, renderItem, }) => {
8
+ const useLogic = ({ organizations = [], onChangeSearch, onChange, currentOrganizationGroupLabel, currentOrganization, groupBy, onClose, onOpen, isOpen: isOpenPopover, isDisabled, disabledReason, isHidePersonalData, action, isLoading, isError, onRetry, renderItem, renderPreview, }) => {
9
9
  const { isLoading: isDashboardLoading } = (0, react_1.useContext)(DashboardLayout_1.DashboardContext);
10
10
  const [searchValue, setSearchValue] = (0, react_1.useState)('');
11
11
  const [anchorButtonEl, setAnchorButtonEl] = (0, react_1.useState)(null);
@@ -53,6 +53,9 @@ const useLogic = ({ organizations = [], onChangeSearch, onChange, currentOrganiz
53
53
  }
54
54
  return !isError && !isLoading && organizations.length > 10;
55
55
  }, [searchValue.length, isError, isLoading, organizations.length]);
56
+ const renderPreviewWrapper = (0, react_1.useMemo)(() => renderPreview
57
+ ? (organization, params) => renderPreview(organization, params)
58
+ : undefined, [renderPreview]);
56
59
  return {
57
60
  action,
58
61
  isLoading,
@@ -67,6 +70,7 @@ const useLogic = ({ organizations = [], onChangeSearch, onChange, currentOrganiz
67
70
  isDisabled,
68
71
  disabledReason,
69
72
  isHidePersonalData,
73
+ renderPreview: renderPreviewWrapper,
70
74
  ref: buttonRef,
71
75
  },
72
76
  searchProps: {
@@ -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,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
- setSearchValue(event.target.value);
34
- onInputChange(event.target.value);
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
- const isNoResult = Boolean(searchValue) && !options.length;
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
- const isShowSearchSuggestion = Boolean((!searchValue && minSymbolsToFetch > 0) ||
59
- (searchValue && searchValue.length < minSymbolsToFetch));
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;